From edf90de445fb07b5d1a3eb69d09b0577ce39bfc7 Mon Sep 17 00:00:00 2001 From: Nikolay Kostyurin Date: Mon, 4 Jun 2018 23:18:50 +0200 Subject: [PATCH] initial --- lerna.json | 7 + package-lock.json | 255 ++++ package.json | 5 + packages/bbob-parser/.gitignore | 4 + packages/bbob-parser/Parser.js | 182 +++ packages/bbob-parser/Parser.test.js | 12 + packages/bbob-parser/Tokenizer.js | 214 +++ packages/bbob-parser/Tokenizer.test.js | 14 + packages/bbob-parser/benchmark/OldParser.js | 215 +++ .../bbob-parser/benchmark/parser_test_new.js | 19 + .../bbob-parser/benchmark/parser_test_old.js | 15 + packages/bbob-parser/benchmark/test/stub.js | 1359 +++++++++++++++++ packages/bbob-parser/char.js | 26 + packages/bbob-parser/index.js | 1 + packages/bbob-parser/package.json | 17 + packages/bbob-parser/parse.js | 11 + packages/bbob-parser/parse.test.js | 23 + packages/bbob-parser/token.js | 24 + 18 files changed, 2403 insertions(+) create mode 100644 lerna.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 packages/bbob-parser/.gitignore create mode 100644 packages/bbob-parser/Parser.js create mode 100644 packages/bbob-parser/Parser.test.js create mode 100644 packages/bbob-parser/Tokenizer.js create mode 100644 packages/bbob-parser/Tokenizer.test.js create mode 100644 packages/bbob-parser/benchmark/OldParser.js create mode 100644 packages/bbob-parser/benchmark/parser_test_new.js create mode 100644 packages/bbob-parser/benchmark/parser_test_old.js create mode 100644 packages/bbob-parser/benchmark/test/stub.js create mode 100644 packages/bbob-parser/char.js create mode 100644 packages/bbob-parser/index.js create mode 100644 packages/bbob-parser/package.json create mode 100644 packages/bbob-parser/parse.js create mode 100644 packages/bbob-parser/parse.test.js create mode 100644 packages/bbob-parser/token.js diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..77629c1 --- /dev/null +++ b/lerna.json @@ -0,0 +1,7 @@ +{ + "lerna": "2.11.0", + "packages": [ + "packages/bbob-parser" + ], + "version": "independent" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..38ad928 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,255 @@ +{ + "name": "bibob", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assertion-error": { + "version": "1.1.0", + "resolved": "https://npm.wsmgroup.ru/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://npm.wsmgroup.ru/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://npm.wsmgroup.ru/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://npm.wsmgroup.ru/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "chai": { + "version": "4.1.2", + "resolved": "https://npm.wsmgroup.ru/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "dev": true, + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://npm.wsmgroup.ru/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "commander": { + "version": "2.15.1", + "resolved": "https://npm.wsmgroup.ru/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://npm.wsmgroup.ru/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://npm.wsmgroup.ru/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://npm.wsmgroup.ru/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://npm.wsmgroup.ru/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://npm.wsmgroup.ru/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://npm.wsmgroup.ru/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://npm.wsmgroup.ru/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://npm.wsmgroup.ru/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://npm.wsmgroup.ru/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://npm.wsmgroup.ru/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "he": { + "version": "1.1.1", + "resolved": "https://npm.wsmgroup.ru/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://npm.wsmgroup.ru/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://npm.wsmgroup.ru/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://npm.wsmgroup.ru/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://npm.wsmgroup.ru/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://npm.wsmgroup.ru/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://npm.wsmgroup.ru/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://npm.wsmgroup.ru/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://npm.wsmgroup.ru/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://npm.wsmgroup.ru/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pathval": { + "version": "1.1.0", + "resolved": "https://npm.wsmgroup.ru/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "dev": true + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://npm.wsmgroup.ru/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://npm.wsmgroup.ru/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "typescript": { + "version": "2.9.1", + "resolved": "https://npm.wsmgroup.ru/typescript/-/typescript-2.9.1.tgz", + "integrity": "sha512-h6pM2f/GDchCFlldnriOhs1QHuwbnmj6/v7499eMHqPeW4V2G0elua2eIc2nu8v2NdHV0Gm+tzX83Hr6nUFjQA==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://npm.wsmgroup.ru/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..dd1511a --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "lerna": "^2.11.0" + } +} diff --git a/packages/bbob-parser/.gitignore b/packages/bbob-parser/.gitignore new file mode 100644 index 0000000..8572739 --- /dev/null +++ b/packages/bbob-parser/.gitignore @@ -0,0 +1,4 @@ +package-lock.json +coverage +lib +dist \ No newline at end of file diff --git a/packages/bbob-parser/Parser.js b/packages/bbob-parser/Parser.js new file mode 100644 index 0000000..ba957e1 --- /dev/null +++ b/packages/bbob-parser/Parser.js @@ -0,0 +1,182 @@ +const Tokenizer = require("./Tokenizer"); +const TokenType = Tokenizer.TYPE; +const TokenChar = Tokenizer.CHAR; +const getCharCode = Tokenizer.getCharCode; + +const isTextToken = (token) => { + const type = token[Tokenizer.TOKEN.TYPE_ID]; + + return type === TokenType.SPACE || type === TokenType.NEW_LINE || type === TokenType.WORD +}; + +const isTagToken = (token) => token[Tokenizer.TOKEN.TYPE_ID] === TokenType.TAG; + +const isTagStart = (token) => !isTagEnd(token); + +const isTagEnd = (token) => getTokenValue(token).charCodeAt(0) === TokenChar.SLASH; + +const isAttrNameToken = (token) => token[Tokenizer.TOKEN.TYPE_ID] === TokenType.ATTR_NAME; + +const isAttrValueToken = (token) => token[Tokenizer.TOKEN.TYPE_ID] === TokenType.ATTR_VALUE; + +const getTagName = (token) => { + const value = getTokenValue(token); + + return isTagEnd(token) ? value.slice(1) : value +}; + +const convertTagToText = (token) => { + let text = getCharCode(TokenChar.OPEN_BRAKET); + + if (isTagEnd(token)) { + text += getCharCode(TokenChar.SLASH) + } + + text += getTokenValue(token); + text += getCharCode(TokenChar.CLOSE_BRAKET); + + return text +}; + +const getTokenValue = (token) => token[Tokenizer.TOKEN.VALUE_ID]; + +const createTagNode = (name, attrs = {}, content = []) => ({tag: name, attrs, content}); + +/** + * +{ + tag: 'div', + attrs: { + class: 'foo' + }, + content: ['hello world!'] +} + */ +module.exports = class Parser { + constructor(tokens, options = {}) { + this.tokens = tokens; + this.options = options + } + + parse() { + const tokens = this.tokens; + const nodes = []; + const nestedNodes = []; + const curTags = []; + const curTagsAttrName = []; + + const getCurTag = () => { + if (curTags.length) { + return curTags[curTags.length - 1] + } + + return null + }; + + const createCurTag = (token) => { + curTags.push(createTagNode(getTokenValue(token))) + }; + + 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 (this.isCloseTag(getTokenValue(token))) { + nestedNodes.push(getCurTag()) + } else { + getNodes().push(getCurTag()); + clearCurTag() + } + } + + // [/tag] + if (isTagEnd(token)) { + clearCurTag(); + + const lastNestedNode = nestedNodes.pop(); + + if (lastNestedNode) { + getNodes().push(lastNestedNode) + } else { + debugger; + console.warn(`Inconsistent tag '${getTokenValue(token)}'`); + } + } + } else { + getNodes().push(convertTagToText(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)) + } + } + + return nodes + } + + isCloseTag(value) { + return this.options.closableTags && this.options.closableTags.indexOf(value) >= 0 + } + + isAllowedTag(value) { + if (this.options.allowOnlyTags && this.options.allowOnlyTags.length) { + return this.options.allowOnlyTags.indexOf(value) >= 0 + } + + return true + } +}; \ No newline at end of file diff --git a/packages/bbob-parser/Parser.test.js b/packages/bbob-parser/Parser.test.js new file mode 100644 index 0000000..a675de4 --- /dev/null +++ b/packages/bbob-parser/Parser.test.js @@ -0,0 +1,12 @@ +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'] + ]); + + }) +}); \ No newline at end of file diff --git a/packages/bbob-parser/Tokenizer.js b/packages/bbob-parser/Tokenizer.js new file mode 100644 index 0000000..7713c33 --- /dev/null +++ b/packages/bbob-parser/Tokenizer.js @@ -0,0 +1,214 @@ +const CHAR = require('./char'); +const TOKEN = require('./token'); + +// const TOKEN.TYPE_ID = 0; +// const TOKEN.VALUE_ID = 1; +// const TOKEN.LINE_ID = 2; +// const TOKEN.COLUMN_ID = 3; +// +// const TOKEN.TYPE_WORD = 'word'; +// const TOKEN.TYPE_TAG = 'tag'; +// const TOKEN.TYPE_ATTR_NAME = 'attr-name'; +// const TOKEN.TYPE_ATTR_VALUE = 'attr-value'; +// const TOKEN.TYPE_SPACE = 'space'; +// const TOKEN.TYPE_NEW_LINE = 'new-line'; + +const getCharCode = String.fromCharCode; + +class Tokenizer { + constructor(input) { + this.buffer = input; + this.colPos = 0; + this.rowPos = 0; + this.index = 0; + } + + tokenize() { + let wordToken = this.createWordToken(''); + let tagToken = null; + let attrNameToken = null; + let attrValueToken = null; + let attrTokens = []; + let tokens = new Array(Math.floor(this.buffer.length / 2)); + let tokenIndex = -1; + + const flushWord = () => { + if (wordToken[TOKEN.VALUE_ID]) { + tokenIndex++; + tokens[tokenIndex] = wordToken; + wordToken = this.createWordToken('') + } + }; + + const flushTag = () => { + if (tagToken !== null) { + tokenIndex++; + tokens[tokenIndex] = tagToken; + tagToken = null; + } + }; + + const flushAttrName = () => { + if (attrNameToken) { + attrTokens.push(attrNameToken); + attrNameToken = null; + } + }; + + const flushAttrValue = () => { + if (attrValueToken) { + attrTokens.push(attrValueToken); + attrValueToken = null + } + }; + + const flushAttrs = () => { + if (attrTokens.length) { + attrTokens.forEach(attrToken => { + tokenIndex++; + tokens[tokenIndex] = attrToken + }); + + attrTokens = []; + } + }; + + // console.time('Lexer.tokenize'); + + while (this.index < this.buffer.length) { + const charCode = this.buffer.charCodeAt(this.index); + + switch (charCode) { + case CHAR.TAB: + case CHAR.SPACE: + flushWord(); + + if (tagToken) { + attrNameToken = this.createAttrNameToken(''); + } + + const spaceCode = charCode === CHAR.TAB ? ' ' : ' '; + + tokenIndex++; + tokens[tokenIndex] = this.createSpaceToken(spaceCode); + + this.colPos++; + break; + + case CHAR.N: + flushWord(); + tokenIndex++; + tokens[tokenIndex] = this.createNewLineToken(getCharCode(charCode)); + + this.rowPos++; + this.colPos = 0; + break; + + case CHAR.OPEN_BRAKET: + flushWord(); + tagToken = this.createTagToken(''); + + this.colPos++; + break; + + case CHAR.CLOSE_BRAKET: + flushTag(); + flushAttrName(); + flushAttrValue(); + flushAttrs(); + + this.colPos++; + break; + + case CHAR.EQ: + if (tagToken) { + attrValueToken = this.createAttrValueToken('') + } else { + wordToken[TOKEN.VALUE_ID] += getCharCode(charCode); + } + + this.colPos++; + break; + + case CHAR.QUOTEMARK: + if (attrValueToken && attrValueToken[TOKEN.VALUE_ID] > 0) { + flushAttrName(); + flushAttrValue(); + } else if (tagToken === null) { + wordToken[TOKEN.VALUE_ID] += getCharCode(charCode); + } + + this.colPos++; + break; + + default: + if (tagToken && attrValueToken) { + attrValueToken[TOKEN.VALUE_ID] += getCharCode(charCode) + } else if (tagToken && attrNameToken) { + attrNameToken[TOKEN.VALUE_ID] += getCharCode(charCode) + } else if (tagToken) { + tagToken[TOKEN.VALUE_ID] += getCharCode(charCode) + } else { + wordToken[TOKEN.VALUE_ID] += getCharCode(charCode); + } + + this.colPos++; + break; + } + + this.index++; + } + + flushWord(); + + tokens.length = tokenIndex; + + return tokens; + } + + createWordToken(value) { + return [TOKEN.TYPE_WORD, value, this.colPos, this.rowPos] + } + + createTagToken(value) { + return [TOKEN.TYPE_TAG, value, this.colPos, this.rowPos] + } + + createAttrNameToken(value) { + return [TOKEN.TYPE_ATTR_NAME, value, this.colPos, this.rowPos] + } + + createAttrValueToken(value) { + return [TOKEN.TYPE_ATTR_VALUE, value, this.colPos, this.rowPos] + } + + createSpaceToken(value) { + return [TOKEN.TYPE_SPACE, value, this.colPos, this.rowPos] + } + + createNewLineToken(value) { + return [TOKEN.TYPE_NEW_LINE, value, this.colPos, this.rowPos] + } +} + +// warm up tokenizer to elimitate code branches that never execute +new Tokenizer(`[b param="hello"]Sample text[/b]\n\t[Chorus]`).tokenize(); + +module.exports = Tokenizer; +module.exports.CHAR = CHAR; +module.exports.TYPE = { + WORD: TOKEN.TYPE_WORD, + TAG: TOKEN.TYPE_TAG, + ATTR_NAME: TOKEN.TYPE_ATTR_NAME, + ATTR_VALUE: TOKEN.TYPE_ATTR_VALUE, + SPACE: TOKEN.TYPE_SPACE, + NEW_LINE: TOKEN.TYPE_NEW_LINE, +}; +module.exports.TOKEN = { + TYPE_ID: TOKEN.TYPE_ID, + VALUE_ID: TOKEN.VALUE_ID, + LINE_ID: TOKEN.LINE_ID, + COLUMN_ID: TOKEN.COLUMN_ID, +}; +module.exports.getCharCode = getCharCode; + diff --git a/packages/bbob-parser/Tokenizer.test.js b/packages/bbob-parser/Tokenizer.test.js new file mode 100644 index 0000000..0908708 --- /dev/null +++ b/packages/bbob-parser/Tokenizer.test.js @@ -0,0 +1,14 @@ +const Tokenizer = require('./Tokenizer'); + +describe("Tokenizer", () => { + it("tokenize single tag", () => { + const input = `[SingleTag]`; + + const tokens = new Tokenizer(input).tokenize(); + + console.log('tokens', tokens); + + expect(tokens).toBeInstanceOf(Array); + expect(tokens[0]).toEqual(['tag', 'SingleTag', 0, 0]) + }) +}); \ No newline at end of file diff --git a/packages/bbob-parser/benchmark/OldParser.js b/packages/bbob-parser/benchmark/OldParser.js new file mode 100644 index 0000000..78b1559 --- /dev/null +++ b/packages/bbob-parser/benchmark/OldParser.js @@ -0,0 +1,215 @@ + +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 + }, +} diff --git a/packages/bbob-parser/benchmark/parser_test_new.js b/packages/bbob-parser/benchmark/parser_test_new.js new file mode 100644 index 0000000..00c33e3 --- /dev/null +++ b/packages/bbob-parser/benchmark/parser_test_new.js @@ -0,0 +1,19 @@ +const parse = require('../index'); + +const options = { + closableTags: ['ch', 'syllable', 'tab'] +}; + +const textStub = require("./test/stub"); + +const count = 10; +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)); \ No newline at end of file diff --git a/packages/bbob-parser/benchmark/parser_test_old.js b/packages/bbob-parser/benchmark/parser_test_old.js new file mode 100644 index 0000000..38e9a09 --- /dev/null +++ b/packages/bbob-parser/benchmark/parser_test_old.js @@ -0,0 +1,15 @@ +const OldParser = require('./OldParser') + +const textStub = require("./test/stub"); + +const count = 10; +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)); + diff --git a/packages/bbob-parser/benchmark/test/stub.js b/packages/bbob-parser/benchmark/test/stub.js new file mode 100644 index 0000000..a488265 --- /dev/null +++ b/packages/bbob-parser/benchmark/test/stub.js @@ -0,0 +1,1359 @@ +module.exports = `#----------------------------------PLEASE NOTE---------------------------------# + This file is the author's own work and represents their interpretation of the + song. You may only use this file for private study, scholarship, or research. + IF THIS WILL BE USED FOR COVER SONG FOR(YOUTUBE,ANY SITE)Pls include the TABS' + LINK IN THE VIDEO DESCPITION. +#------------------------------------------------------------------------------# + +Paramore : After Laughter +Transcriber : Francis Sario +Site : https://www.facebook.com/francistwister.sario +Transcribed by Francis Sario (PARAMORE PHILIPPINES) + +My Dear fellow fans .I can't afford any Paramore merch here in the Philippines, + if you think you can Donate to me. Email me at francissario@gmail.com .I really love them since 2006. + + +Hi to Ms Jeanne na mahilig sa oreo :) + + + + + + +CHORDS - PARAMORE : AFTER LAUGHTER (ALBUM) + +1. "Hard Times" +2. "Rose-Colored Boy" +3. "Told You So" +4. "Forgiveness" +5. "Fake Happy" +6. "26" +7. "Pool" +8. "Grudges" +9. "Caught in the Middle" +10. "Idle Worship" +11. "No Friend" +12. "Tell Me How" + + + + +------------------------------------------------------------------------------------------------- +1. "Hard Times" +------------------------------------------------------------------------------------------------- + +Tuning: Standard + +[Intro] +[ch]Eb[/ch] [ch]Fm[/ch] | [ch]Ab[/ch] [ch]Cm[/ch] | [ch]Eb[/ch] [ch]Fm[/ch] | [ch]Cm[/ch] [ch]Ab[/ch] + +[Verse 1] +[ch]Eb[/ch] [ch]Fm[/ch] + All that I want +[ch]Ab[/ch] [ch]Cm[/ch] + Is to wake up fine +[ch]Eb[/ch] [ch]Fm[/ch] + Tell me that I’m alright +[ch]Cm[/ch] [ch]Ab[/ch] + That I ain’t gonna die +[ch]Eb[/ch] [ch]Fm[/ch] + All that I want +[ch]Ab[/ch] [ch]Cm[/ch] + Is a hole in the ground +[ch]Eb[/ch] [ch]Fm[/ch] + You can tell me when it’s alright +[ch]Cm[/ch] [ch]Ab[/ch] + For me to come out + +[ch]Ab[/ch] [ch]Bb[/ch] [ch]Cm[/ch] [ch]Eb[/ch] + +[Chorus] + [ch]Fm[/ch] +Hard times + [ch]Cm[/ch] +Gonna make you wonder why you even try + [ch]Eb[/ch] +Hard times + [ch]Ab[/ch] +Gonna take you down and laugh when you cry + [ch]Fm[/ch] +These lives + [ch]Cm[/ch] +And I still don't know how I even survive + [ch]Eb[/ch] [ch]Ab[/ch] +Hard times, Hard times +[ch]Cm[/ch] +And I gotta get to rock bottom + +[Interlude] +[ch]Eb[/ch] [ch]Fm[/ch] | [ch]Ab[/ch] [ch]Cm[/ch] | [ch]Eb[/ch] [ch]Fm[/ch] | [ch]Cm[/ch] [ch]Ab[/ch] + +[Verse 2] +[ch]Eb[/ch] [ch]Fm[/ch] + I'm walking around +[ch]Ab[/ch] [ch]Cm[/ch] + With my little raincloud +[ch]Eb[/ch] [ch]Fm[/ch] + Hanging over my head +[ch]Cm[/ch] [ch]Ab[/ch] + And it ain’t coming down +[ch]Eb[/ch] [ch]Fm[/ch] + Where do I go? +[ch]Ab[/ch] [ch]Cm[/ch] + Gimme some sort of sign +[ch]Eb[/ch] [ch]Fm[/ch] + Hit me with lightning! +[ch]Cm[/ch] [ch]Ab[/ch] + Maybe I’ll come alive + +[ch]Ab[/ch] [ch]Bb[/ch] [ch]Cm[/ch] [ch]Eb[/ch] + +[Chorus] + [ch]Fm[/ch] +Hard times + [ch]Cm[/ch] +Gonna make you wonder why you even try + [ch]Eb[/ch] +Hard times + [ch]Ab[/ch] +Gonna take you down and laugh when you cry + [ch]Fm[/ch] +These lives + [ch]Cm[/ch] +And I still don't know how I even survive + [ch]Eb[/ch] [ch]Ab[/ch] +Hard times, Hard times +[ch]Cm[/ch] +And I gotta get to rock bottom + +[Bridge] +[ch]Eb[/ch] +Tell my friends I’m coming down + [ch]Ab[/ch] +We'll kick it when I hit the ground +[ch]Eb[/ch] +Tell my friends I’m coming down + [ch]Ab[/ch] +We'll kick it when I hit the ground +[ch]Eb[/ch] + When I hit the ground +[ch]Ab[/ch] + When I hit the ground +[ch]Eb[/ch] + When I hit the ground +[ch]Ab[/ch] + When I hit the ground + +[Chorus] + [ch]Fm[/ch] +Hard times + [ch]Cm[/ch] +Gonna make you wonder why you even try + [ch]Eb[/ch] +Hard times + [ch]Ab[/ch] +Gonna take you down and laugh when you cry + [ch]Fm[/ch] +These lives + [ch]Cm[/ch] +And I still don't know how I even survive + [ch]Eb[/ch] [ch]Ab[/ch] +Hard times, Hard times + +[Chorus] + [ch]Fm[/ch] +Hard times + [ch]Cm[/ch] +Gonna make you wonder why you even try + [ch]Eb[/ch] +Hard times + [ch]Ab[/ch] +Gonna take you down and laugh when you cry + [ch]Fm[/ch] +These lives + [ch]Cm[/ch] +And I still don't know how I even survive + [ch]Eb[/ch] [ch]Ab[/ch] +Hard times, Hard times + +[Outro] +[ch]Fm[/ch] [ch]Cm[/ch] [ch]Eb[/ch] [ch]Ab[/ch] 2x [ch]Cm[/ch] + And I gotta get to rock bottom + +-------------------------------------------------------------------------------------------------- +2. "Rose-Colored Boy" +-------------------------------------------------------------------------------------------------- +Tuning : Standard +Intro: + +[ch]F[/ch] +[ch]Dm[/ch] [ch]C[/ch] [ch]F[/ch] +Low-key, no pressure, just hang with me and my weather + [ch]Dm[/ch] [ch]C[/ch] +Low-key, no pressure, just hang with me and my weather + +Verse: +[ch]F[/ch] +Rose-colored boy + [ch]Dm[/ch] +I hear you making all that noise + [ch]C[/ch] +About the world you want to see + [ch]F[/ch] +And oh, I'm so annoyed + [ch]Dm[/ch] [ch]C[/ch] +'Cause I just killed off what was left of the optimist in me + + +Refrain: + [ch]Bb[/ch] [ch]C[/ch] [ch]Dm[/ch] +Hearts are breaking, Wars are raging on + [ch]C[/ch] +And I have taken my glasses off +[ch]Bb[/ch] [ch]C[/ch] +You got me nervous + [ch]Dm[/ch] +I'm right at the end of my rope + [ch]C[/ch] +A half empty girl + [ch]F[/ch] +Don't make me laugh, I'll choke + +Chorus- + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] +Just let me cry a little bit longer + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] [ch]C[/ch] +I ain't gon' smile if I don't want to + [ch]Bb[/ch] [ch]C[/ch] +Hey man, we all can't be like you + + [ch]Bb[/ch] [ch]C[/ch] +I wish we were all rose-colored too + + [ch]F[/ch] +My rose-colored boy + [ch]Dm[/ch] [ch]C[/ch] +Low-key, no pressure, just hang with me and my weather + + +Verse: +[ch]F[/ch] [ch]Dm[/ch] [ch]C[/ch] +I want you to stop insisting that I'm not a lost cause + [ch]F[/ch] +'Cause I've been through a lot + [ch]Dm[/ch] +Really all I've got is just to stay pissed off +[ch]C[/ch] +If it's all right by you +[ch]Bb[/ch] [ch]C[/ch] [ch]Dm[/ch] +Hearts are breaking, wars are raging on + [ch]F[/ch] +And I have taken my glasses off +[ch]Bb[/ch] [ch]C[/ch] +You got me nervous + [ch]Dm[/ch] +And you’re turning it into a joke + [ch]C[/ch] +A half empty girl + [ch]F[/ch] +Don't make me laugh, I'll... + + +Chorus- + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] +Just let me cry a little bit longer + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] [ch]C[/ch] +I ain't gon' smile if I don't want to + [ch]Bb[/ch] [ch]C[/ch] +Hey man, we all can't be like you + + [ch]Bb[/ch] [ch]C[/ch] +I wish we were all rose-colored too + + [ch]Bb[/ch] [ch]C[/ch] +My rose-colored boy + +[ch]Bb[/ch] +Leave me here a little bit longer + [ch]C[/ch] +I think I wanna stay in the car +I don't want anybody seeing me cry now +[ch]Bb[/ch] +You say "We gotta look on the bright side" + [ch]C[/ch] +I say "Well maybe if you wanna go blind" +You say my eyes are getting too dark now +But boy, you ain't ever seen my mind + + +Chorus- + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] +Just let me cry a little bit longer + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] [ch]C[/ch] +I ain't gon' smile if I don't want to + [ch]Bb[/ch] [ch]C[/ch] +Hey man, we all can't be like you + + [ch]Bb[/ch] [ch]C[/ch] +I wish we were all rose-colored too +My rose-colored boy + + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] +Just let me cry a little bit longer + [ch]F[/ch] [ch]Am[/ch] [ch]Bb[/ch] [ch]C[/ch] +I ain't gon' smile if I don't want to + [ch]Bb[/ch] [ch]C[/ch] +Nah, we all can't be like you + [ch]Bb[/ch] [ch]C[/ch] +I wish we were all rose-colored too + + [ch]F[/ch] +My rose-colored boy +[ch]Dm[/ch] [ch]C[/ch] [ch]F[/ch] +Low-key, no pressure, just hang with me and my weather +[ch]Dm[/ch] [ch]C[/ch] +Low-key, no pressure, just hang with me and my weather + +-------------------------------------------------------------------------------------------------- +3. "Told You So" +-------------------------------------------------------------------------------------------------- +Tuning: Standard +[Verse 1] + + [ch]A#[/ch] +For all I know +[ch]Dm[/ch] [ch]Dm[/ch] + The best is over and + [ch]A#[/ch] [ch]Dm[/ch] +The worst is yet to come + [ch]A#[/ch] +Is it enough +[ch]Dm[/ch] [ch]Dm[/ch] + To keep on hoping + [ch]A#[/ch] [ch]Dm[/ch] +When the rest have given up +And they go + +[Chorus] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + But they love to say they told me so +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + They love to say they told me + +[Tag] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + You say, you say you told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] + +[Instrumental] + +[ch]A#[/ch] [ch]Dm[/ch] [ch]A#[/ch] [ch]Dm[/ch] + +[Verse 2] + [ch]A#[/ch] +I know you like +[ch]Dm[/ch] [ch]Dm[/ch] [ch]A#[/ch] + When I admit that I was wrong + [ch]Dm[/ch] +And you were right + [ch]A#[/ch] +At least I try +[ch]Dm[/ch] [ch]Dm[/ch] + To keep my cool + [ch]A#[/ch] [ch]Dm[/ch] +When I'm thrown into a fire +And they go + +[Chorus] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + But they love to say they told me so +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + They love to say they told me + +[Tag] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + You say, you say you told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] + +[Bridge] + +[ch]C[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]Dm[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]C[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]Dm[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]A#[/ch] +Throw me into the fire + [ch]Gm[/ch] +Throw me and pull me out again +[ch]Dm[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]C[/ch] +Throw me into the fire +Throw me and pull me out again +[ch]Dm[/ch] +Throw me into the fire +Throw me and pull me out again +N.C. +Throw me into the fire +Throw me and pull me out again + +[Instrumental] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] +[ch]A#[/ch] [ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + +[Chorus] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + But they love to say they told me so +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] + I hate to say I told you so +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] + They love to say they told me + +[Tag] + +[ch]A#[/ch] [ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + Say they say they told me +[ch]Gm[/ch] [ch]Dm[/ch] [ch]F[/ch] [ch]A#[/ch] + You say, you say you told me +[ch]C[/ch] [ch]Dm[/ch] [ch]F[/ch] +-------------------------------------------------------------------------------------------------- +4. "Forgiveness" +-------------------------------------------------------------------------------------------------- +Tuning: Standard + +Intro: [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] 4x + +Verse: +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +You hurt me bad this time, no coming back +[ch]F#m[/ch] [ch]G[/ch] +And I cry 'till I couldn't cry, another heart attack +[ch]F#m[/ch] [ch]G[/ch] [ch]A[/ch] +If I lay on the floor, maybe I'll wake up +[ch]D[/ch] [ch]A[/ch] +And I don't pick up when you call + [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +'Cause your voice is a gun +[ch]F#m[/ch] [ch]G[/ch] +Every word is a bullet hole +Shot a hole in the sun + [ch]F#m[/ch] [ch]G[/ch] [ch]A[/ch] +If I never look up maybe I'll never notice + + +Chorus: + [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +And you, you want forgiveness + [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +But I, I just can't do it yet + +Verse: +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +There's still a thread that runs from your body to mine + [ch]F#m[/ch] [ch]G[/ch] [ch]A[/ch] +And you can't break what you don't see, an invisible line + [ch]F#m[/ch] [ch]G[/ch] [ch]A[/ch] +If I follow it down would we just be alright? + [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] [ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +But it could take me all your life to learn to love + [ch]F#m[/ch] [ch]G[/ch] +How I thought I could love someone + [ch]G[/ch] +I haven't even begun +[ch]F#m[/ch] [ch]G[/ch] [ch]A[/ch] +If it's all up to us we might as well give up + + +Chorus: +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +And you, you want forgiveness +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +(I can barely hang on to myself) +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +But I, I can't give you that +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +(I can't give you, I can't give you that) +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +And you, you want forgiveness +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +(I'm afraid that I'll have nothing left) +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +But I, I just can't do it yet +[ch]D[/ch] [ch]A[/ch] [ch]G[/ch] +(I can't do, I just can't do it yet) + +Interlude: [ch]A[/ch] [ch]G[/ch] 4x + +Bridge: +[ch]F#m[/ch] [ch]G[/ch] +Don't you go and get it twisted +[ch]F#m[/ch] [ch]G[/ch] +Forgiving is not forgetting +[ch]F#m[/ch] [ch]Bm[/ch] +Don't you go and get it twisted +[ch]A[/ch] +Forgiving is not forgetting + [ch]A[/ch] +No, it's not forgetting + [ch]A[/ch] +No, I'll never forget it, no + +Do Chorus +And you, you want forgiveness +(I can barely hang on to myself) +But I, I can't give you that +(I can't give you, I can't give you that) +And you, you want forgiveness +(I'm afraid that I'll have nothing left) +But I, I just can't do it yet +(I can't do, I just can't do it yet) + +End : D +-------------------------------------------------------------------------------------------------- +5. "Fake Happy" +-------------------------------------------------------------------------------------------------- +[Intro] + +[ch]G[/ch] [ch]D[/ch] [ch]Em[/ch] +I love making you believe + [ch]C[/ch] [ch]G[/ch] +What you get is what you see + [ch]D[/ch] [ch]Em[/ch] +But I'm so fake happy + [ch]C[/ch] +I feel so fake happy +[ch]G[/ch] [ch]D[/ch] [ch]Em[/ch] +And I bet everybody here + [ch]C[/ch] +Is just as insincere +[ch]G[/ch] [ch]D[/ch] +We're all so fake happy + [ch]Em[/ch] [ch]C[/ch] +And I know fake happy + +Intro 2: [ch]C[/ch] [ch]C[/ch] [ch]C[/ch] [ch]C[/ch] [ch]Bm[/ch] [ch]C[/ch] [ch]Bm[/ch] 4x + +[Verse] +[ch]C[/ch] +I been doing a good job of makin' 'em think +I'm quite alright +But I hope I don't blink +You see its easy when I'm stomping on a beat +But no one sees me when I crawl back underneath + +[Pre-Chorus] C ( Do intro 2) +If I smile with my teeth +Bet you believe me +If I smile with my teeth +I think I believe me + +[Chorus] + [ch]G[/ch] +Oh please don't ask me how I've been + [ch]C[/ch] +Don't make me play pretend + [ch]C[/ch] +Oh no, oh what's the use +[ch]Bm[/ch] [ch]C[/ch] [ch]Em[/ch] [ch]D[/ch] [ch]G[/ch] +Oh please, I bet everybody here is fake happy too + +[ch]C[/ch] [ch]C[/ch] [ch]C[/ch] [ch]C[/ch] [ch]Bm[/ch] [ch]C[/ch] [ch]Bm[/ch] 2x + +[Verse 2] +[ch]C[/ch] + +And if I go out tonight, dress up my fears +You think I'll look alright with these mascara tears? +See I'm gonna draw my lipstick wider than my mouth +And if the lights are low they'll never see me frown + +[Pre-Chorus] C ( Do intro 2) +If I smile with my teeth +Bet you believe me +If I smile with my teeth +I think I believe me + +[Chorus] + [ch]G[/ch] +Oh please don't ask me how I've been + [ch]C[/ch] +Don't make me play pretend + [ch]C[/ch] +Oh no, oh what's the use +[ch]Bm[/ch] [ch]C[/ch] Em. [ch]D[/ch] [ch]G[/ch] +Oh please, I bet everybody here is fake happy too + + +[Bridge] +[ch]G[/ch] [ch]A[/ch] [ch]Bm[/ch] [ch]C[/ch] [ch]C[/ch] +I know I said that I was doing good and that I'm happy now +Oh oh +[ch]Bm[/ch] [ch]C[/ch] [ch]D[/ch] [ch]G[/ch] +I should've known that when things are going good thats when I get knocked down + +[ch]G[/ch] [ch]A[/ch] [ch]Bm[/ch] [ch]C[/ch] C(badapbadapbapbap) +[ch]Bm[/ch] [ch]C[/ch] [ch]D[/ch] [ch]Em[/ch] [ch]G[/ch] (badapbadapbapbap) + +[Chorus] Do Chorus +Oh please, just don't ask me how I've been +Don't make me play pretend +Oh no, oh no +Oh what's the use? +Oh please, I bet everybody here is fake happy too +Oh please I bet everybody here is fake happy too +-------------------------------------------------------------------------------------------------- +6. "26" +-------------------------------------------------------------------------------------------------- +Standard + + +Intro: [ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] 2x + +Verse: +[ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] [ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] +Man, you really know how to get someone down +[ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] [ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] +Everything was fine, until you came around + +Refrain: + [ch]A/C#[/ch] [ch]G[/ch] +And I’ve been chasing after dreamers in the clouds + [ch]A/C#[/ch] [ch]G[/ch] +After all wasn’t I the one who said + [ch]G[/ch] [ch]G/B[/ch] [ch]G/A[/ch] [ch]Em[/ch] +To keep your feet on the ground +Man, you really brought me back down + +Chorus: +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Hold onto hope if you got it +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Don’t let it go for nobody +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +And they say that dreaming is free +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +But I wouldn’t care what it cost me + +Verse: +[ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] [ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] +You got me tied up but I stay close to the window +[ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] [ch]D[/ch] [ch]G[/ch] [ch]Bm[/ch] +And I talk to myself about the places that I used to go + [ch]A/C#[/ch] [ch]G[/ch] +And hope that someday that I just float away + [ch]A/C#[/ch] [ch]G[/ch] +And forget every cynical thing you said + [ch]G/B[/ch] [ch]G/A[/ch] [ch]Em[/ch] +When you gonna hear me out +Man, you really bring me down + +Chorus: +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Hold onto hope if you got it +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Don’t let it go for nobody +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +And they say that dreaming is free +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +But I wouldn’t care what it cost me + +Bridge: + +[ch]Em[/ch] [ch]A[/ch] + Reality will break your heartk your +[ch]Em[/ch] [ch]A[/ch] + Survival will not be the hardest + [ch]Em[/ch] [ch]A[/ch] + It’s keeping all your hopes alive +[ch]Em[/ch] [ch]A[/ch] + When all the rest of you has died +So let it break your heart + +Chorus: +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Hold onto hope if you got it +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Don’t let it go for nobody +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +And they say that dreaming is free +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +But I wouldn’t care what it cost me + +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Hold onto hope if you got it +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +Don’t let it go for nobody +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +And they say that dreaming is free +[ch]G[/ch] [ch]D[/ch] [ch]A[/ch] +But I wouldn’t care what it cost me + +G/B-G/A-Em 4x + +-------------------------------------------------------------------------------------------------- +7. "Pool" +-------------------------------------------------------------------------------------------------- +Intro: [ch]F[/ch] [ch]C[/ch] [ch]G[/ch] 4x + +Verse: + [ch]F[/ch] +As if the first cut wasn’t deep enough + [ch]C[/ch] [ch]G[/ch] +I dove in again ’cause I’m not into giving up + [ch]F[/ch] +Could’ve gotten the same rush from any lover’s touch + [ch]C[/ch] +Why get used to something new? + [ch]G[/ch] +‘Cause no one breaks my heart like you + +Verse : +[ch]F[/ch] +And you kiss me, and you wish we could see what happens next +[ch]C[/ch] [ch]G[/ch] +For a moment, I can forget what happens in my head +[ch]F[/ch] +If I doubt you, will you come through with a happy second chance? +[ch]C[/ch] +A happy ending +[ch]G[/ch] +But this time you don’t leave me sinking + +Chorus: +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +I’m underwater +With no air in my lungs +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +My eyes are open +And I'm giving up +[ch]C[/ch] [ch]Em[/ch] [ch]F[/ch] +You are the wave +I could never tame +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +If I survive +I’ll dive back in + +[ch]F[/ch] +As if the first blood didn’t thrill enough + [ch]C[/ch] [ch]G[/ch] +I went further out to see what else was left of us + [ch]F[/ch] +Never found the deep end of our little ocean + [ch]C[/ch] +Drain the fantasy of you + [ch]G[/ch] +Headfirst into shallow pools + +[ch]F[/ch] +And I wonder, is it better to get it over with? + [ch]C[/ch] [ch]G[/ch] +The illusion, can shatter before we begin +[ch]F[/ch] +If you’re really sorry + [ch]C[/ch] +Happy second chance, I think I could forgive +[ch]G[/ch] +This time you won’t leave me sinking + + + +Chorus: +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +I’m underwater +With no air in my lungs +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +My eyes are open +And I'm giving up +[ch]C[/ch] [ch]Em[/ch] [ch]F[/ch] +You are the wave +I could never tame +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +If I survive +I’ll dive back in + +Bridge : +[ch]F[/ch] +Dive back into +Right back into +[ch]C[/ch] [ch]G[/ch] +Dive right back into you +Now I know you +[ch]F[/ch] +Now that I know you +Now that I know you +[ch]C[/ch] [ch]C[/ch] +Dive right back into you +[ch]F[/ch] +Dive back into +Right back into +[ch]C[/ch] [ch]G[/ch] +Dive right back into you +Now I know you +[ch]F[/ch] +Now that I know you +Now that I know you +[ch]C[/ch] [ch]C[/ch] +Dive right back into you + +Chorus: +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +I’m underwater +With no air in my lungs +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +My eyes are open +And I'm giving up +[ch]C[/ch] [ch]Em[/ch] [ch]F[/ch] +You are the wave +I could never tame +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +If I survive + +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +I’m underwater +With no air in my lungs +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +My eyes are open +I’m done giving up +[ch]C[/ch] [ch]Em[/ch] [ch]F[/ch] +You are the wave +I could never tame +[ch]C[/ch] [ch]G[/ch] [ch]F[/ch] +If I survive +[ch]F[/ch] +I’ll dive back in +[ch]F[/ch] +I’ll dive back in +-------------------------------------------------------------------------------------------------- +8. "Grudges" +-------------------------------------------------------------------------------------------------- +Intro - [ch]D[/ch] [ch]G[/ch] [ch]A[/ch] [ch]G[/ch] 4x + + +Verse - + [ch]D[/ch] [ch]G[/ch] [ch]A[/ch] [ch]G[/ch] +Strange how we found ourselves exactly where we left off + [ch]D[/ch] [ch]G[/ch] [ch]A[/ch] +I know you’re shaking my hand like it is the first time + [ch]G[/ch] +Are we alright? + +Refrain: + [ch]D[/ch] [ch]G[/ch] +Are you recounting all my faults? + [ch]A[/ch] +And are you racking your brain just to find them all? + [ch]D[/ch] [ch]Em[/ch] [ch]F#m[/ch] [ch]G[/ch] +Could it be that I’ve changed or did you? + +Chorus: +[ch]D[/ch] [ch]G[/ch] +Stop asking why + [ch]A[/ch] [ch]G[/ch] +Why we had to waste so much time + [ch]D[/ch] [ch]G[/ch] +Well, we just pick up, pick up and start again + [ch]A[/ch] [ch]G[/ch] +‘Cause we can’t keep holding on to grudges + +Verse: +[ch]D[/ch] [ch]G[/ch] [ch]A[/ch] [ch]G[/ch] +Time is a bastard I won’t break my neck to get around it + [ch]D[/ch] [ch]G[/ch] +But aren’t we so brave to give up a fight + [ch]A[/ch] [ch]G[/ch] +And let the years go by without us + +Refrain: +[ch]D[/ch] [ch]G[/ch] +‘Cause now I feel you by my side +[ch]A[/ch] [ch]G[/ch] +And I don’t even care if it’s been a while + [ch]D[/ch] [ch]Em[/ch] [ch]F#m[/ch] [ch]G[/ch] +I can feel that we’ve changed and we’re better this way + + +Chorus: +[ch]D[/ch] [ch]G[/ch] +Stop asking why + [ch]A[/ch] [ch]G[/ch] +Why we had to waste so much time + [ch]D[/ch] [ch]G[/ch] +Well, we just pick up, pick up and start again + [ch]A[/ch] [ch]G[/ch] +‘Cause we can’t keep holding on to grudges + +Chorus 2: +[ch]F#m[/ch] [ch]G[/ch] +And if you wanna call me up or come over + [ch]A[/ch] [ch]A[/ch] +Come on, we’ll laugh ’till we cry +[ch]D[/ch] [ch]G[/ch] +Like we did when we were kids + [ch]A[/ch] [ch]G[/ch] +‘Cause we can’t keep holding on to grudges + +Bridge: +[ch]G[/ch] [ch]F#m[/ch] [ch]A[/ch] +Why did it take so long? +[ch]G[/ch] [ch]F#m[/ch] [ch]A[/ch] +Why did it take so long? +[ch]G[/ch] [ch]F#m[/ch] [ch]A[/ch] +Why did it take us so long to just let go? +[ch]G[/ch] [ch]F#m[/ch] [ch]A[/ch] +Why did it take us so long to stop holding on? + +Chorus: +[ch]D[/ch] [ch]G[/ch] +Stop asking why + [ch]A[/ch] [ch]G[/ch] +Why we had to waste so much time + [ch]D[/ch] [ch]G[/ch] +Well, we just pick up, pick up and start again + [ch]A[/ch] [ch]G[/ch] +‘Cause we can’t keep holding on to grudges + +Chorus 2: +[ch]F#m[/ch] [ch]G[/ch] +And if you wanna call me up or come over + [ch]A[/ch] [ch]A[/ch] +Come on, we’ll laugh ’till we cry +[ch]D[/ch] [ch]G[/ch] +Like we did when we were kids + [ch]A[/ch] [ch]G[/ch] +‘Cause we can’t keep holding on to grudges + +Outro - [ch]D[/ch] [ch]G[/ch] [ch]A[/ch] [ch]G[/ch] 4x +We can’t keep holding on to grudges +Could it be that I’ve changed or did you? +-------------------------------------------------------------------------------------------------- +9. "Caught in the Middle" +-------------------------------------------------------------------------------------------------- +Intro" [ch]C[/ch] [ch]F[/ch] [ch]G[/ch] 2x +[ch]C[/ch] +I can’t think of getting old +[ch]F[/ch] [ch]G[/ch] [ch]C[/ch] [ch]F[/ch] [ch]G[/ch] +It only makes me want to die +[ch]C[/ch] +And I can’t think of who I was +[ch]F[/ch] [ch]G[/ch] [ch]C[/ch] [ch]F[/ch] [ch]G[/ch] +‘Cause it just makes me want to cry, cry, cry + + +Refrain: + [ch]F[/ch] [ch]Em[/ch] +Can’t look back, can’t look too far ahead + [ch]F[/ch] [ch]G[/ch] +I got the point, I got the message + + +Chorus: +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I’m just a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I try to keep going but it’s not that simple +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I think I’m a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] +Gotta keep going or they’ll call me a quitter + [ch]Em[/ch] [ch]F[/ch] +Yeah I’m caught in the middle + +[ch]C[/ch] [ch]F[/ch] [ch]G[/ch] 2x + +Verse: +[ch]C[/ch] +I was dreaming life away +[ch]F[/ch] [ch]G[/ch] [ch]C[/ch] [ch]F[/ch] [ch]G[/ch] +All the while just going blind +[ch]C[/ch] +Can’t see the forest for the trees +[ch]F[/ch] [ch]G[/ch] [ch]C[/ch] [ch]F[/ch] [ch]G[/ch] +Behind the lids of my own eyes + +Refrain : + [ch]F[/ch] [ch]Em[/ch] +Nostalgia’s cool, but it won’t help me now + [ch]F[/ch] [ch]G[/ch] +A dream is good, if you don’t wear it out + +Chorus: +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I’m just a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I try to keep going but it’s not that simple +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I think I’m a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] +Gotta keep going or they’ll call me a quitter + [ch]Em[/ch] [ch]F[/ch] +Yeah I’m caught in the middle + +Solo- +[ch]C[/ch] [ch]Am[/ch] [ch]F[/ch] [ch]F[/ch] +[ch]C[/ch] +No, I don’t need no help +[ch]Am[/ch] +I can sabotage me by myself +[ch]F[/ch] +Don’t need no one else +[ch]F[/ch] +I can sabotage me by myself + +[ch]C[/ch] +I don’t need no help +[ch]Am[/ch] +I can sabotage me by myself +[ch]F[/ch] +Don’t need no one else +[ch]F[/ch] +I can sabotage me by myself (repeat) + +Chorus: +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I’m just a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I try to keep going but it’s not that simple +[ch]C[/ch] [ch]Am[/ch] [ch]Em[/ch] [ch]F[/ch] +I think I’m a little bit caught in the middle +[ch]C[/ch] [ch]Am[/ch] +Gotta keep going or they’ll call me a quitter + [ch]Em[/ch] [ch]F[/ch] +Yeah I’m caught in the middle + +[ch]C[/ch] [ch]F[/ch] [ch]G[/ch] + +I’m caught in the middle +-------------------------------------------------------------------------------------------------- +10. "Idle Worship" +-------------------------------------------------------------------------------------------------- +[Intro] +[ch]D#[/ch] [ch]D/Bb[/ch] +[Verse 1] +[ch]D#[/ch] +Standing here like I'm supposed to say something +[ch]D/Bb[/ch] +Don't hold your breath, I never said I'd save you, honey +[ch]G#[/ch] +And I don't want your money. If I was you, I'd run from me + [ch]Gm[/ch] [ch]A#[/ch] +or rip me open. You'll see you're not the only one who's hopeless. +[ch]D#[/ch] +Be sure to put your faith in something more +[ch]D/Bb[/ch] +I'm just a girl and you're not as alone as you feel +[ch]G#[/ch] +We all got problems, don't we? We all need heroes, don't we? +[ch]Gm[/ch] [ch]A#[/ch] +But rest assured, there's not a single person here who's worthy + +[Pre-Chorus] +[ch]D#[/ch] [ch]D/Bb[/ch] [ch]Gm[/ch] +La, la la la la la la +Don't let me let you down + + +[Chorus] +[ch]G#[/ch] [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] + Hey baby, I'm not your superhuman +[ch]D#[/ch] [ch]A#[/ch] +And if that's what you want + [ch]D#[/ch] [ch]G#[/ch] +I hate to let you down + [ch]D#[/ch] [ch]A#[/ch] +I got your hopes up + [ch]D#[/ch] [ch]G#[/ch] +Now I got you hoping + [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] +That I'm gonna be the one to let you down + + +[Verse 2] +[ch]D#[/ch] +Oh, it's such a long and awful lonely fall +[ch]D/Bb[/ch] +Down from this pedestal that you keep putting me on +[ch]G#[/ch] +What if I fall on my face? What if I make a mistake? +[ch]Gm[/ch] [ch]A#[/ch] +If it's okay a little grace would be appreciated +[ch]D#[/ch] +Remember how we used to like ourselves? +[ch]D/Bb[/ch] +What little light that's left, we need to keep it sacred +[ch]G#[/ch] +I know that you're afraid to let all the dark escape ya +[ch]Gm[/ch] [ch]A#[/ch] +But we can let the light illuminate these hopeless places + +[Pre-Chorus] +[ch]D#[/ch] [ch]D/Bb[/ch] [ch]Gm[/ch] +La, la la la la la la + [ch]G#[/ch] [ch]A#[/ch] +Just let me let you down + + +[Chorus] +[ch]G#[/ch] [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] + Hey baby, I'm not your superhuman +[ch]D#[/ch] [ch]A#[/ch] +And if that's what you want + [ch]D#[/ch] [ch]G#[/ch] +I hate to let you down + [ch]D#[/ch] [ch]A#[/ch] +I got your hopes up + [ch]D#[/ch] [ch]G#[/ch] +Now I got you hoping + [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] +That I'm gonna be the one to let you down + +[Bridge] +[ch]Cm[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] +La la la la la la +[ch]Cm[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] +La la la la la la +[ch]Cm[/ch] [ch]A#[/ch] +Oh, no, I ain't your hero +[ch]D#[/ch] [ch]G#[/ch] +You're wasting all your faith on me +[ch]Cm[/ch] [ch]A#[/ch] +Oh, no, I know where this goes +[ch]D#[/ch] [ch]G#[/ch] +They can say, they say, your savior doesn't look a thing like me + + +[Pre-Chorus] +[ch]D#[/ch] [ch]D/Bb[/ch] [ch]Cm[/ch] [ch]Gm[/ch] +La, la la la la la la +Don't let me let you down + +[Chorus] +[ch]G#[/ch] [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] + Hey baby, I'm not your superhuman +[ch]D#[/ch] [ch]A#[/ch] +And if that's what you want + [ch]D#[/ch] [ch]G#[/ch] +I hate to let you down + [ch]D#[/ch] [ch]A#[/ch] +I got your hopes up + [ch]D#[/ch] [ch]G#[/ch] +Now I got you hoping + [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] +That I'm gonna be the one to let you down +[ch]G#[/ch] [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] [ch]G#[/ch] + Hey baby, I'm not your superhuman +[ch]D#[/ch] [ch]A#[/ch] +And if that's what you want + [ch]D#[/ch] [ch]G#[/ch] +I hate to let you down + [ch]D#[/ch] [ch]A#[/ch] +I got your hopes up + [ch]D#[/ch] [ch]G#[/ch] +Now I got you hoping + [ch]D#[/ch] [ch]A#[/ch] [ch]D#[/ch] +That I'm gonna be the one to let you down +-------------------------------------------------------------------------------------------------- +11. "No Friend" +-------------------------------------------------------------------------------------------------- +[ch]Cm[/ch] [ch]G#[/ch] [ch]Gm[/ch] ( all througout) + +These old letters from years ago +I felt it was leading to a +[...] +When I wrote this [...] I may have been finally able to address how it feels + +Another brick-red room, +Another black-top town, +Another misspelled band burning their own houses down, +Another pine-box tune to fill the cemetery day +Another star, a touch of orange over purgatory gray, + +Another thorny field to scatter fruitless seed, +Another song that runs too long god knows no one needs +More misguided ghosts, more transparent hands +To drop a nickel in our basket and we'll do our riot! + +Dance beneath another burning sky, +Behind our painted lips +In scores of catatonic smile-covered ankle-bitten ships +So throw your pedestal of stone in the forgetful sea +As protection from the paper-thin perfection +You project on me + +When this repetition ends behind the window shades, +A semi-conscious sorrow sleeping in the bed I've made, +That most unrestful bed, that most original of sins +And you'll say that's what I get when I let ambition win again + +I'd hate to let you down +So I'll let the waters rise +And drown my dull reflection +In the naïve expectation in your eyes +Back in a cast bit-part, +Back when I felt most free, +I had a butcher's heart and no one thought they knew me + +So before the regiment resumes, +Before the dreaded sun appears, +My driver's waiting +So let's make one point crystal clear: + +You see a flood-lit form, +I see a shirt design, +I'm no savior of yours +And you're no friend of mine. + +You're no friend of mine +You're no friend of mine +I'm no savior of yours and you're no friend of mine + +You see a flood-lit form +I see a shirt design +I'm no savior of yours +And you're no friend of mine + +I see myself in the reflection of people's eyes +Realising what they see may not be even close to the image I see in myself +And I hate I might actually be more afraid +[...] I feel like they know the story +I saw a bear floating in the river and thought it was a fur coat +Twelve years ago I stood on the shore +Jumped in and grabbed the coat +And the river is rushing toward a waterfall +And my friend stood at the shore and shouted to let go of the coat and swim back to land +I let go of the coat but the coat won't let go of me +In any case please let me know if there's more I can give you +If nothing comes of it, then just know we are grateful + + +-------------------------------------------------------------------------------------------------- +12. "Tell Me How" +-------------------------------------------------------------------------------------------------- +Intro: + [ch]BbM7[/ch] [ch]C[/ch] + [ch]BbM7[/ch] [ch]Am7[/ch] + [ch]F[/ch] [ch]Am7[/ch] + [ch]F[/ch] +Verse: + [ch]C[/ch] [ch]BbM7[/ch] +I can't call you a stranger + [ch]C[/ch] [ch]BbM7[/ch] +But I can't call you + [ch]Am7[/ch] [ch]F[/ch] +I know you think that I erased you + [ch]Am7[/ch] [ch]F[/ch] +You may hate me but I can't hate you + [ch]C[/ch] +And I won't replace you + +Chorus +[ch]BbM7[/ch] [ch]C[/ch] +Tell me how to feel about you now +[ch]BbM7[/ch] +Tell me how to feel about you now + [ch]Am7[/ch] [ch]F[/ch] + Oh, let me know + [ch]Am7[/ch] [ch]F[/ch] (C) +Do I suffocate or let go? + +Verse- + [ch]BbM7[/ch] +Think I'm tired of getting over it + [ch]C[/ch] [ch]BbM7[/ch] +Just starting something new again + [ch]Am7[/ch] [ch]F[/ch] +I'm getting sick of the beginnings + [ch]Am7[/ch] [ch]F[/ch] +And always coming to your defences + [ch]C[/ch] [ch]BbM7[/ch] +I guess it's good to get it off my chest + [ch]C[/ch] [ch]BbM7[/ch] +I guess I can't believe I haven't yet + [ch]Am7[/ch] [ch]F[/ch] +You know I got my own convictions +[ch]Am7[/ch] [ch]F[/ch] +And they're stronger than any addiction + ( C) +But no one's winning + + +Chorus +[ch]BbM7[/ch] [ch]C[/ch] +Tell me how to feel about you now +[ch]BbM7[/ch] +Tell me how to feel about you now + [ch]Am7[/ch] [ch]F[/ch] + Oh, let me know + [ch]Am7[/ch] [ch]F[/ch] (C) +Do I suffocate or let go? + + +(Repeat chorus) + +Interlude: Gm Dm-C Am 2x + [ch]Gm[/ch] +You keep me up with your silence +[ch]Dm[/ch] [ch]C[/ch] +Take me down with your quiet + [ch]Am[/ch] [ch]Gm[/ch] +Of all the weapons you fight with + [ch]Dm[/ch] [ch]C[/ch] +Your silence is the most violent + +(DO Chorus 2x) + +Outro: +[ch]BbM7[/ch] [ch]C[/ch] + [ch]BbM7[/ch] [ch]Am7[/ch] + [ch]F[/ch] [ch]Am7[/ch] + [ch]F[/ch] [ch]C[/ch] + +You don't have to tell me +If you ever think of me +I know you say you're busy +In the wild fog of your memory +You don't have to tell me +I can still believe + +End : BbM7" + +-------------------------------------------------------------------------------------------------- + +`; \ No newline at end of file diff --git a/packages/bbob-parser/char.js b/packages/bbob-parser/char.js new file mode 100644 index 0000000..6f4dd82 --- /dev/null +++ b/packages/bbob-parser/char.js @@ -0,0 +1,26 @@ +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 OPEN_BRAKET = "[".charCodeAt(0); +const CLOSE_BRAKET = "]".charCodeAt(0); + +const SLASH = "/".charCodeAt(0); + +module.exports = { + N, + F, + R, + TAB, + EQ, + QUOTEMARK, + SPACE, + OPEN_BRAKET, + CLOSE_BRAKET, + SLASH +}; \ No newline at end of file diff --git a/packages/bbob-parser/index.js b/packages/bbob-parser/index.js new file mode 100644 index 0000000..88fa820 --- /dev/null +++ b/packages/bbob-parser/index.js @@ -0,0 +1 @@ +module.exports = require('./parse'); \ No newline at end of file diff --git a/packages/bbob-parser/package.json b/packages/bbob-parser/package.json new file mode 100644 index 0000000..5a2be68 --- /dev/null +++ b/packages/bbob-parser/package.json @@ -0,0 +1,17 @@ +{ + "name": "bbob", + "version": "1.0.0", + "description": "Fast BB Code parser written in pure javascript, no dependencies", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "jest" + }, + "author": "Nikolay Kostyurin ", + "license": "MIT", + "devDependencies": { + "jest": "^23.1.0" + } +} diff --git a/packages/bbob-parser/parse.js b/packages/bbob-parser/parse.js new file mode 100644 index 0000000..bc6e725 --- /dev/null +++ b/packages/bbob-parser/parse.js @@ -0,0 +1,11 @@ +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(); + + return ast +}; \ No newline at end of file diff --git a/packages/bbob-parser/parse.test.js b/packages/bbob-parser/parse.test.js new file mode 100644 index 0000000..5d0d336 --- /dev/null +++ b/packages/bbob-parser/parse.test.js @@ -0,0 +1,23 @@ +const parse = require('./index'); +const OldParser = require('./benchmark/OldParser'); +const tabText = require('./benchmark/test/stub'); + +const options = { + closableTags: ['ch', 'syllable', 'tab'], + allowOnlyTags: ['ch', 'syllable', 'tab'], +}; + +describe("parse", () => { + test("tag with spaces", () => { + const ast = parse(`[Verse 2]`); + + expect(ast).toEqual([{tag: 'Verse 2', attrs: {}, content: []}]); + }); + + test("same as old parser", () => { + const ast1 = parse(tabText, options); + const ast2 = OldParser.parse(tabText); + + expect(ast1).toEqual(ast2); + }) +}); \ No newline at end of file diff --git a/packages/bbob-parser/token.js b/packages/bbob-parser/token.js new file mode 100644 index 0000000..9a22fba --- /dev/null +++ b/packages/bbob-parser/token.js @@ -0,0 +1,24 @@ +const TOKEN_TYPE_ID = 0; +const TOKEN_VALUE_ID = 1; +const TOKEN_LINE_ID = 2; +const TOKEN_COLUMN_ID = 3; + +const TOKEN_TYPE_WORD = 'word'; +const TOKEN_TYPE_TAG = 'tag'; +const TOKEN_TYPE_ATTR_NAME = 'attr-name'; +const TOKEN_TYPE_ATTR_VALUE = 'attr-value'; +const TOKEN_TYPE_SPACE = 'space'; +const TOKEN_TYPE_NEW_LINE = 'new-line'; + +module.exports = { + TYPE_ID: TOKEN_TYPE_ID, + VALUE_ID: TOKEN_VALUE_ID, + LINE_ID: TOKEN_LINE_ID, + COLUMN_ID: TOKEN_COLUMN_ID, + TYPE_WORD: TOKEN_TYPE_WORD, + TYPE_TAG: TOKEN_TYPE_TAG, + TYPE_ATTR_NAME: TOKEN_TYPE_ATTR_NAME, + TYPE_ATTR_VALUE: TOKEN_TYPE_ATTR_VALUE, + TYPE_SPACE: TOKEN_TYPE_SPACE, + TYPE_NEW_LINE: TOKEN_TYPE_NEW_LINE +}; \ No newline at end of file