From 81bfbc00d920c6601c93f8efe783071e0f4fb504 Mon Sep 17 00:00:00 2001 From: spat-ne-hochu Date: Sun, 19 Jun 2016 03:30:29 +0300 Subject: [PATCH 1/2] copy from old repo --- .gitignore | 22 +--------------- index.js | 49 ++++++++++++++++++++++++++++++++++++ line-analyze.js | 29 +++++++++++++++++++++ node-parse.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 23 +++++++++++++++++ 5 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 index.js create mode 100644 line-analyze.js create mode 100644 node-parse.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 5148e52..e7c8932 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,17 @@ -# Logs logs *.log npm-debug.log* - -# Runtime data pids *.pid *.seed - -# Directory for instrumented libs generated by jscoverage/JSCover lib-cov - -# Coverage directory used by tools like istanbul coverage - -# nyc test coverage .nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt - -# node-waf configuration .lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) build/Release - -# Dependency directories node_modules jspm_packages - -# Optional npm cache directory .npm - -# Optional REPL history .node_repl_history +.idea diff --git a/index.js b/index.js new file mode 100644 index 0000000..7dc773b --- /dev/null +++ b/index.js @@ -0,0 +1,49 @@ +'use strict'; + +var nodeParse = require('./node-parse'); + +/** + * + * @param string + * + * @returns {BSNode} + */ +module.exports = function(string) { + let state = new ParseState(string), + stack = []; + + try { + do { + let node = nodeParse(state); + + if (state.indent > 0) { + stack[state.indent - 1].addChild(node); + } + + stack[state.indent] = node; + } while (!state.isEOF); + } catch (e) { + console.error(state, e); + throw 'parse error'; + } + + return stack[0]; +}; + +class ParseState { + constructor(string) { + this.string = string; + this.lastBlockName = null; + this.index = 0; + this.line = 0; + this.isEOF = false; + } + + execRegexp(regexp) { + regexp.lastIndex = this.index; + let result = regexp.exec(this.string); + this.index = regexp.lastIndex; + + return result; + } +} diff --git a/line-analyze.js b/line-analyze.js new file mode 100644 index 0000000..fa76e25 --- /dev/null +++ b/line-analyze.js @@ -0,0 +1,29 @@ +"use strict"; + +module.exports = function(state) { + var spaces = 0, + beforeIndex = state.index; + + while (state.string[state.index] === ' ') { + spaces++; + state.index++; + } + + var isNode; + + if (spaces % 4 === 0) { + isNode = true; + } else if(spaces % 4 === 2) { + isNode = false; + } else { + throw 'invalid indent' + } + + return { + beforeIndex: beforeIndex, + indent: Math.floor(spaces / 4), + isNode: isNode, + isProperty: !isNode, + isEmpty: state.string[state.index] === '\n' + } +}; diff --git a/node-parse.js b/node-parse.js new file mode 100644 index 0000000..5bb2e51 --- /dev/null +++ b/node-parse.js @@ -0,0 +1,67 @@ +const CombineNode = require('@combinejs/node'), + lineAnalyze = require('./line-analyze'), + directiveProvider = require('../providers/directive'); + +module.exports = function(state) { + let {indent} = lineAnalyze(state); + + let node = new CombineNode(nameParse(state)); + + if (node.isBlock) { + state.lastBlockName = node.blockName; + } else { + node.blockName = state.lastBlockName; + } + + do { + let {isProperty, beforeIndex, indent: lineIndent} = lineAnalyze(state); + + if (! isProperty) { + state.index = beforeIndex; + break; + } + + if (lineIndent === indent) { + let {NS, name, value} = propertyParse(state); + + if (NS === 'rule') { + let ConcreteDirective = directiveProvider(name); + + node.addDirective(name, new ConcreteDirective(value, node)); + } else { + node.setProp(NS, name, value); + } + } + + } while (! state.isEOF); + + state.indent = indent; + + return node; +}; + +function nameParse(state) { + let result = state.execRegexp(/([a-z][a-z0-9]+)/gi); + LexemeEnding(state); + + return result[1]; +} + +function propertyParse(state) { + let result = state.execRegexp(/([a-z][a-z0-9]+)\.([a-z][a-z0-9]+)\s*=\s*'([^']+)'/gi); + LexemeEnding(state); + + return { + NS: result[1], + name: result[2], + value: result[3] + } +} + +function LexemeEnding(state) { + let result = state.execRegexp(/\s*(\r?\n)+/mg); + + if (result === null || state.index >= state.string.length - 1) { + state.isEOF = true; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..80fb012 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "@combinejs/parser", + "version": "0.1.0", + "description": "default combinejs parser", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/combinejs/parser.git" + }, + "author": "spat.ne.hochu", + "license": "ISC", + "bugs": { + "url": "https://github.com/combinejs/parser/issues" + }, + "homepage": "https://github.com/combinejs/parser#readme", + "dependencies": { + "@combinejs/match-directive": "^0.1.0", + "@combinejs/node": "^0.1.1" + } +} From 66c8f42dca3d8e46d1722ee131cdd8630f6dec0f Mon Sep 17 00:00:00 2001 From: spat-ne-hochu Date: Sun, 19 Jun 2016 06:34:32 +0300 Subject: [PATCH 2/2] initial parser --- .travis.yml | 18 ++++ index.js | 10 +- line-analyze.js => lib/line-analyze.js | 8 +- node-parse.js => lib/node-parse.js | 5 +- package.json | 19 +++- test/index.js | 24 +++++ test/table-beauty.comb | 22 +++++ test/table-beauty.json | 128 +++++++++++++++++++++++++ 8 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 .travis.yml rename line-analyze.js => lib/line-analyze.js (73%) rename node-parse.js => lib/node-parse.js (92%) create mode 100644 test/index.js create mode 100644 test/table-beauty.comb create mode 100644 test/table-beauty.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2775518 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +sudo: false + +cache: + directories: + - node_modules + +language: node_js +node_js: + - "6" + +before_script: + - "npm i -D" + +script: + - "npm run coverage" + +after_script: + - "cat ./coverage/lcov.info | coveralls" \ No newline at end of file diff --git a/index.js b/index.js index 7dc773b..56e272b 100644 --- a/index.js +++ b/index.js @@ -1,14 +1,18 @@ 'use strict'; -var nodeParse = require('./node-parse'); +var nodeParse = require('./lib/node-parse'); /** * * @param string * - * @returns {BSNode} + * @returns {CombineNode} */ module.exports = function(string) { + if (typeof string !== 'string') { + throw 'parse need string'; + } + let state = new ParseState(string), stack = []; @@ -21,7 +25,7 @@ module.exports = function(string) { } stack[state.indent] = node; - } while (!state.isEOF); + } while (! state.isEOF); } catch (e) { console.error(state, e); throw 'parse error'; diff --git a/line-analyze.js b/lib/line-analyze.js similarity index 73% rename from line-analyze.js rename to lib/line-analyze.js index fa76e25..ed723db 100644 --- a/line-analyze.js +++ b/lib/line-analyze.js @@ -21,9 +21,9 @@ module.exports = function(state) { return { beforeIndex: beforeIndex, - indent: Math.floor(spaces / 4), - isNode: isNode, - isProperty: !isNode, - isEmpty: state.string[state.index] === '\n' + indent: Math.floor(spaces / 4), + isNode: isNode, + isProperty: ! isNode, + isEmpty: state.string[state.index] === '\n' } }; diff --git a/node-parse.js b/lib/node-parse.js similarity index 92% rename from node-parse.js rename to lib/node-parse.js index 5bb2e51..d4f3061 100644 --- a/node-parse.js +++ b/lib/node-parse.js @@ -1,6 +1,7 @@ const CombineNode = require('@combinejs/node'), - lineAnalyze = require('./line-analyze'), - directiveProvider = require('../providers/directive'); + directiveProvider = require('@combinejs/directives-provider'), + lineAnalyze = require('./line-analyze'); + module.exports = function(state) { let {indent} = lineAnalyze(state); diff --git a/package.json b/package.json index 80fb012..f51c3c0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "default combinejs parser", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "mocha", + "coverage": "istanbul cover _mocha -- -R spec" }, "repository": { "type": "git", @@ -17,7 +18,21 @@ }, "homepage": "https://github.com/combinejs/parser#readme", "dependencies": { + "@combinejs/directives-provider": "^0.9.0", "@combinejs/match-directive": "^0.1.0", "@combinejs/node": "^0.1.1" - } + }, + "devDependencies": { + "chai": "^3.5.0", + "coveralls": "^2.11.9", + "istanbul": "^0.4.3", + "mocha": "^2.5.3" + }, + "files": [ + "LICENSE", + "History.md", + "Readme.md", + "index.js", + "lib/" + ] } diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..5f1fa48 --- /dev/null +++ b/test/index.js @@ -0,0 +1,24 @@ +/* global before, after, beforeEach, describe, it */ + +const assert = require('chai').assert, + fs = require('fs'), + path = require('path'), + parse = require('../index'); + + +describe('parser tests', function() { + let block, json; + + before('load test block', function() { + block = fs.readFileSync(path.resolve('./test/table-beauty.comb')).toString(); + json = JSON.parse(fs.readFileSync(path.resolve('./test/table-beauty.json'))); + }); + + it('parse table block', function() { + let tree = parse(block); + + tree = JSON.parse(JSON.stringify(tree)); + + assert.deepEqual(tree, json); + }); +}); \ No newline at end of file diff --git a/test/table-beauty.comb b/test/table-beauty.comb new file mode 100644 index 0000000..e32ca88 --- /dev/null +++ b/test/table-beauty.comb @@ -0,0 +1,22 @@ +TableBeauty + html.tag = 'table' + css.display = 'table' + + row + rule.match = '*' + html.tag = 'tr' + css.display = 'table-row' + + cell + rule.match = '*' + html.tag = 'td' + css.display = 'table-cell' + + head + rule.match = '1' + css.background = '#eee' + css.fontWeight = '900' + + headCell + rule.match = '*' + html.tag = 'th' diff --git a/test/table-beauty.json b/test/table-beauty.json new file mode 100644 index 0000000..a3897b5 --- /dev/null +++ b/test/table-beauty.json @@ -0,0 +1,128 @@ +{ + "name": "TableBeauty", + "blockName": "TableBeauty", + "elementName": "", + "isBlock": true, + "isElement": false, + "_props": { + "html": { + "tag": "table" + }, + "css": { + "display": "table" + } + }, + "_content": [ + { + "name": "row", + "blockName": "TableBeauty", + "elementName": "row", + "isBlock": false, + "isElement": true, + "_props": { + "html": { + "tag": "tr" + }, + "css": { + "display": "table-row" + } + }, + "_content": [ + { + "name": "cell", + "blockName": "TableBeauty", + "elementName": "cell", + "isBlock": false, + "isElement": true, + "_props": { + "html": { + "tag": "td" + }, + "css": { + "display": "table-cell" + } + }, + "_content": [], + "_mixins": [], + "_directives": { + "match": { + "selector": { + "_params": { + "a": null, + "b": null + }, + "_priority": 0 + } + } + } + } + ], + "_mixins": [], + "_directives": { + "match": { + "selector": { + "_params": { + "a": null, + "b": null + }, + "_priority": 0 + } + } + } + }, + { + "name": "head", + "blockName": "TableBeauty", + "elementName": "head", + "isBlock": false, + "isElement": true, + "_props": { + "css": { + "background": "#eee", + "fontWeight": "900" + } + }, + "_content": [ + { + "name": "headCell", + "blockName": "TableBeauty", + "elementName": "headCell", + "isBlock": false, + "isElement": true, + "_props": { + "html": { + "tag": "th" + } + }, + "_content": [], + "_mixins": [], + "_directives": { + "match": { + "selector": { + "_params": { + "a": null, + "b": null + }, + "_priority": 0 + } + } + } + } + ], + "_mixins": [], + "_directives": { + "match": { + "selector": { + "_params": { + "a": 1, + "b": null + }, + "_priority": 3 + } + } + } + } + ], + "_mixins": [], + "_directives": {} +} \ No newline at end of file