caesar-parser
Advanced tools
Comparing version 0.0.1 to 0.1.0
@@ -9,3 +9,3 @@ 'use strict'; | ||
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
@@ -47,15 +47,2 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
_createClass(JsonParser, [{ | ||
key: 'parse', | ||
value: function parse() { | ||
_Parser3['default'].prototype.parse.apply(this, arguments); | ||
if (this.ctx.value !== undefined) { | ||
this._res = this.ctx.value; | ||
} | ||
return this._res; | ||
} | ||
/** | ||
* @override | ||
*/ | ||
}, { | ||
key: 'token', | ||
@@ -71,3 +58,55 @@ value: function token(st) { | ||
} | ||
/** | ||
* @param {number} indentSize - default tabSize | ||
* @returns {Promise} | ||
*/ | ||
}, { | ||
key: 'reformatCode', | ||
value: function reformatCode(indentSize) { | ||
var resCode = []; | ||
indentSize = indentSize || this._tabSize; | ||
var level = 0; | ||
return this._iter(function (stream) { | ||
var indentNum = stream.indentation(); | ||
var str = stream._string.slice(indentNum); | ||
var indentStack = stream.indentStack; | ||
if (str !== '') { | ||
stream.readStack(indentStack, function (type, substr) { | ||
substr = substr.trim(''); | ||
switch (type) { | ||
case 'enter': | ||
resCode.push(substr + '\n'); | ||
level++; | ||
_indent(); | ||
break; | ||
case 'outer': | ||
level--; | ||
resCode.push('\n'); | ||
_indent(substr); | ||
break; | ||
case 'space': | ||
resCode.push(substr + ' '); | ||
break; | ||
case 'align': | ||
resCode.push(substr + '\n'); | ||
_indent(); | ||
break; | ||
default: | ||
resCode.push(substr); | ||
break; | ||
} | ||
}); | ||
} | ||
}, { saveStyle: true }).then(function () { | ||
return resCode.join(''); | ||
}); | ||
function _indent() { | ||
var str = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0]; | ||
resCode.push(Array(indentSize * level).fill(' ').join('') + str); | ||
} | ||
} | ||
}, { | ||
key: '_value', | ||
@@ -79,6 +118,9 @@ value: function _value(st) { | ||
case '{': | ||
st.style('operator', 'enter'); | ||
return this.pushCtx(this._object, { type: 'object', value: {} }); | ||
case '[': | ||
st.style('operator', 'enter'); | ||
return this.pushCtx(this._array, { type: 'array', value: [] }); | ||
case '"': | ||
st.style('string'); | ||
var str = this._string(st); | ||
@@ -88,2 +130,3 @@ this._saveValue(str); | ||
case '-': | ||
st.style('number'); | ||
var num = this._number(st); | ||
@@ -94,5 +137,7 @@ this._saveValue(num); | ||
if (ch >= '0' && ch <= '9') { | ||
st.style('number'); | ||
var num2 = this._number(st); | ||
this._saveValue(num2); | ||
} else { | ||
st.style('keyword'); | ||
var word = this._word(st); | ||
@@ -107,8 +152,12 @@ this._saveValue(word); | ||
var ctx = this.ctx; | ||
if (ctx.type === 'object') { | ||
ctx.value[ctx.key] = value; | ||
} else if (ctx.type === 'array') { | ||
ctx.value.push(value); | ||
var type = ctx.get('type'); | ||
var val = ctx.get('value'); | ||
if (type === 'object') { | ||
val = val.set(ctx.get('key'), value); | ||
this.setCtx('value', val); | ||
} else if (type === 'array') { | ||
val = val.push(value); | ||
this.setCtx('value', val); | ||
} else { | ||
ctx.value = value; | ||
this.setCtx('value', value); | ||
} | ||
@@ -121,12 +170,16 @@ } | ||
if (ch === '}') { | ||
st.style('operator', 'outer'); | ||
this.popCtx(); | ||
this._saveValue(ctx.value); | ||
this._saveValue(ctx.get('value')); | ||
return false; | ||
} else if (ch === '"') { | ||
ctx.key = this._string(st); | ||
st.style('property'); | ||
this.setCtx('key', this._string(st)); | ||
if (st.trimNext() !== ':') { | ||
return this.error('object syntax error.', st.pos); | ||
} | ||
st.style('operator', 'space'); | ||
return this._value(st); | ||
} else if (ch === ',') { | ||
st.style('operator', 'align'); | ||
return false; | ||
@@ -140,15 +193,17 @@ } | ||
var ch = st.trimNext(); | ||
if (ctx.mustEatArrayItem && (ch === ']' || ch === ',')) { | ||
if (ctx.get('mustEatArrayItem') && (ch === ']' || ch === ',')) { | ||
return this.error('array syntax error.', st.pos); | ||
} | ||
if (ch === ']') { | ||
st.style('operator', 'outer'); | ||
this.popCtx(); | ||
this._saveValue(ctx.value); | ||
this._saveValue(ctx.get('value')); | ||
return false; | ||
} else if (ch === ',') { | ||
ctx.mustEatArrayItem = true; | ||
st.style('operator', 'align'); | ||
this.setCtx('mustEatArrayItem', true); | ||
return false; | ||
} | ||
st.backUp(1); | ||
ctx.mustEatArrayItem = false; | ||
this.setCtx('mustEatArrayItem', false); | ||
return this._value(st); | ||
@@ -220,5 +275,4 @@ } | ||
// 非数字时后退 | ||
var peek = st.peek(); | ||
if (peek && peek.trim()) { | ||
st.backUp(); | ||
if (ch) { | ||
st.backUp(1); | ||
} | ||
@@ -225,0 +279,0 @@ if (isNaN(number)) { |
@@ -9,2 +9,4 @@ 'use strict'; | ||
var _get = function get(_x6, _x7, _x8) { var _again = true; _function: while (_again) { var object = _x6, property = _x7, receiver = _x8; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x6 = parent; _x7 = property; _x8 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
@@ -14,2 +16,6 @@ | ||
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
var _events = require('events'); | ||
var _constants = require('./constants'); | ||
@@ -21,6 +27,8 @@ | ||
var _objectAssign = require('object-assign'); | ||
var _Context = require('./Context'); | ||
var _objectAssign2 = _interopRequireDefault(_objectAssign); | ||
var _Context2 = _interopRequireDefault(_Context); | ||
var _utils = require('./utils'); | ||
var _colors = require('colors'); | ||
@@ -32,32 +40,11 @@ | ||
var Context = (function () { | ||
function Context(tokenize, data) { | ||
_classCallCheck(this, Context); | ||
var Parser = (function (_EventEmitter) { | ||
_inherits(Parser, _EventEmitter); | ||
this.pre = null; | ||
this.tokenize = tokenize; | ||
(0, _objectAssign2['default'])(this, data); | ||
} | ||
_createClass(Context, [{ | ||
key: 'push', | ||
value: function push(tokenize, data) { | ||
var ctx = new Context(tokenize, data); | ||
ctx.pre = this; | ||
return ctx; | ||
} | ||
}, { | ||
key: 'pop', | ||
value: function pop() { | ||
return this.pre; | ||
} | ||
}]); | ||
return Context; | ||
})(); | ||
var Parser = (function () { | ||
/** | ||
* @param {string} code | ||
* @param {?object} opts | ||
* { | ||
* tabSize: 4 | ||
* } | ||
*/ | ||
@@ -71,8 +58,7 @@ | ||
this._code = code.split(_constants.ENDED_REG); | ||
_get(Object.getPrototypeOf(Parser.prototype), 'constructor', this).call(this); | ||
this._tabSize = opts.tabSize || 4; | ||
this._res = null; | ||
this._row = 0; | ||
this._errorHandlers = []; | ||
this.ctx = new Context(); | ||
this._initCode(code); | ||
this._delayed = new _utils.Delayed(); | ||
this._readyPause = false; // 触发暂停 | ||
} | ||
@@ -95,2 +81,10 @@ | ||
* @param {function} fn | ||
* @param {object} opts | ||
* { | ||
* {number} startRow - 开始遍历的位置, 默认之前停留的时间 | ||
* {boolean} saveStyle - 是否保存样式 | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* } | ||
* @returns {Promise} | ||
* @private | ||
@@ -101,7 +95,44 @@ */ | ||
value: function _iter(fn) { | ||
this._row = 0; | ||
while (this._row < this._code.length) { | ||
if (fn(this._code[this._row])) break; | ||
this._row++; | ||
} | ||
var _this = this; | ||
var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
this._row = opts.startRow || this._row; | ||
var delayed = opts.delayed ? this._delayed : function (_fn) { | ||
_fn(); | ||
}; | ||
return new Promise(function (res, rej) { | ||
if (_this._row >= _this._code.length) { | ||
return res(true); | ||
} | ||
var errorHandler = function errorHandler(error) { | ||
_this.removeErrorHandler(errorHandler); | ||
rej(error); | ||
}; | ||
_this.onError(errorHandler); | ||
var run = function run() { | ||
if (_this._readyPause) { | ||
_this._readyPause = false; | ||
return; | ||
} | ||
var str = _this._code[_this._row]; | ||
var stream = new _StringStream2['default'](str, { tabSize: _this._tabSize, saveStyle: opts.saveStyle }); | ||
while (!stream.eol()) { | ||
// if return true, break the parser | ||
if (_this.token(stream)) { | ||
_this._res = null; | ||
return; | ||
} | ||
} | ||
_this.emit('parse', fn && fn(stream, _this._row), _this._row); | ||
_this._row++; | ||
if (_this._row < _this._code.length) { | ||
delayed(run, opts.delayTime); | ||
} else { | ||
_this.removeErrorHandler(errorHandler); | ||
res(true); | ||
} | ||
}; | ||
delayed(run, opts.delayTime); | ||
}); | ||
} | ||
@@ -117,4 +148,2 @@ | ||
value: function error(msg, col) { | ||
var _this = this; | ||
var errorMsg = ''; | ||
@@ -136,16 +165,39 @@ var lines = undefined, | ||
lines = lines.concat(afterLines); | ||
this._errorHandlers.forEach(function (fn) { | ||
fn.call(_this, { | ||
message: _colors2['default'].bgBlack('\n' + lines.join('\n')), | ||
text: msg, | ||
row: row, | ||
col: col | ||
}); | ||
this.emit('error', { | ||
message: _colors2['default'].bgBlack('\n' + lines.join('\n')), | ||
text: msg, | ||
row: row, | ||
col: col | ||
}); | ||
return true; | ||
} | ||
}, { | ||
key: 'clearContext', | ||
value: function clearContext() { | ||
this.ctx = new _Context2['default'](); | ||
this._ctxList.length = 0; | ||
this._row = 0; | ||
} | ||
}, { | ||
key: '_initCode', | ||
value: function _initCode(code) { | ||
this._res = null; | ||
this._row = 0; | ||
this._code = code.split(_constants.ENDED_REG); | ||
this.ctx = new _Context2['default'](); | ||
this._ctxList = []; | ||
} | ||
}, { | ||
key: 'onParse', | ||
value: function onParse(fn) { | ||
this.on('parse', fn); | ||
} | ||
}, { | ||
key: 'removeParseHandler', | ||
value: function removeParseHandler(fn) { | ||
this.removeListener('parse', fn); | ||
} | ||
/** | ||
* @param {function} fn | ||
* @examples | ||
*/ | ||
@@ -155,8 +207,86 @@ }, { | ||
value: function onError(fn) { | ||
this._errorHandlers.push(fn); | ||
this.on('error', fn); | ||
} | ||
}, { | ||
key: 'removeErrorHandler', | ||
value: function removeErrorHandler(fn) { | ||
this.removeListener('error', fn); | ||
} | ||
/** | ||
* @param {?string} code - 需要解析的代码, 可以为空 | ||
* @param {object} opts | ||
* {number} startRow - 开始遍历的位置, 默认之前停留的时间 | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* @returns {Promise} | ||
*/ | ||
}, { | ||
key: 'highlight', | ||
value: function highlight(code) { | ||
var _this2 = this; | ||
var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
code && this._initCode(code); | ||
var startRow = opts.startRow || this._row; | ||
if (this._ctxList.length !== 0 && opts.startRow > 0) { | ||
// 获取之前的状态 | ||
this.ctx = this._ctxList[startRow - 1] && this._ctxList[startRow - 1].context; | ||
this._ctxList = this._ctxList.slice(0, startRow); | ||
} else if (startRow === 0) { | ||
this.clearContext(); | ||
} | ||
opts.startRow = startRow; | ||
opts.saveStyle = true; | ||
return this._iter(function (stream) { | ||
var ctx = { | ||
context: _this2.ctx, | ||
styleStack: stream.styleStack, | ||
indentStack: stream.indentStack | ||
}; | ||
_this2._ctxList.push(ctx); | ||
return ctx; | ||
}, opts).then(function () { | ||
_this2._res = _this2.ctx.toJS(); | ||
_this2._row = 0; | ||
return _this2._ctxList.map(function (item) { | ||
return item.styleStack; | ||
}); | ||
}); | ||
} | ||
/** | ||
* @param {number} row | ||
* @param {object} opts | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* @returns {Promise} | ||
*/ | ||
}, { | ||
key: 'highlightFrom', | ||
value: function highlightFrom(row) { | ||
var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
opts.startRow = row; | ||
return this.highlight(null, opts); | ||
} | ||
/** | ||
* 暂停 | ||
*/ | ||
}, { | ||
key: 'pause', | ||
value: function pause() { | ||
this._readyPause = true; | ||
} | ||
}, { | ||
key: 'keepOn', | ||
value: function keepOn(opts) { | ||
return this.highlight(null, opts); | ||
} | ||
/** | ||
* @param {string} code | ||
* @returns {*} parse result | ||
* @returns {Promise} | ||
*/ | ||
@@ -166,16 +296,14 @@ }, { | ||
value: function parse(code) { | ||
var _this2 = this; | ||
var _this3 = this; | ||
code && (this._code = code.split(_constants.ENDED_REG)); | ||
this._iter(function (str) { | ||
var stream = new _StringStream2['default'](str, { tabSize: _this2._tabSize }); | ||
while (!stream.eol()) { | ||
// if return true, break the parse | ||
if (_this2.token(stream)) { | ||
_this2._res = null; | ||
return true; | ||
} | ||
} | ||
this.clearContext(); | ||
return this._iter(function () { | ||
return { context: _this3.ctx }; | ||
}).then(function () { | ||
_this3._res = _this3.ctx.toJS(); | ||
// clear ctx | ||
_this3.clearContext(); | ||
return _this3._res; | ||
}); | ||
return this._res; | ||
} | ||
@@ -195,2 +323,7 @@ }, { | ||
} | ||
}, { | ||
key: 'setCtx', | ||
value: function setCtx(key, value) { | ||
this.ctx = this.ctx.set(key, value); | ||
} | ||
@@ -206,8 +339,13 @@ /** | ||
} | ||
}, { | ||
key: 'row', | ||
get: function get() { | ||
return this._row; | ||
} | ||
}]); | ||
return Parser; | ||
})(); | ||
})(_events.EventEmitter); | ||
exports['default'] = Parser; | ||
module.exports = exports['default']; |
@@ -19,2 +19,3 @@ 'use strict'; | ||
* stringStart: 0, | ||
* saveStyle: false, | ||
* tabSize: 4, | ||
@@ -32,4 +33,7 @@ * } | ||
this._tabSize = opts.tabSize || 4; | ||
this._saveStyle = opts.saveStyle || false; | ||
this._pos = 0; | ||
this._string = string; | ||
this._styleStack = []; | ||
this._indentStack = []; | ||
} | ||
@@ -195,3 +199,43 @@ | ||
} | ||
/** | ||
* @param {string} name - style name, like operator variable | ||
* @param {string} indentCode - 'enter' | 'outer' | 'space' | 'align' | ||
*/ | ||
}, { | ||
key: 'style', | ||
value: function style(name, indentCode) { | ||
if (this._saveStyle) { | ||
this._styleStack.push(this._pos - 1, name); | ||
indentCode && this._indentStack.push(this._pos - 1, indentCode); | ||
} | ||
} | ||
}, { | ||
key: 'readStack', | ||
value: function readStack(stack, fn) { | ||
var _this = this; | ||
var pre = 0; | ||
var substr = undefined; | ||
stack.forEach(function (pos, key) { | ||
if (key % 2 === 0) { | ||
substr = _this._string.slice(pre, pos + 1); | ||
pre = pos + 1; | ||
} else { | ||
fn(pos, substr); | ||
} | ||
}); | ||
if (pre < this._string.length - 1) fn(null, this._string.slice(pre)); | ||
} | ||
}, { | ||
key: 'styleStack', | ||
get: function get() { | ||
return this._styleStack; | ||
} | ||
}, { | ||
key: 'indentStack', | ||
get: function get() { | ||
return this._indentStack; | ||
} | ||
}, { | ||
key: 'pos', | ||
@@ -198,0 +242,0 @@ get: function get() { |
{ | ||
"name": "caesar-parser", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "", | ||
@@ -17,3 +17,4 @@ "main": "lib/index.js", | ||
"build:lib": "babel src --optional es7.decorators --out-dir lib", | ||
"build": "npm run clean && npm run build:lib" | ||
"build": "npm run clean && npm run build:lib", | ||
"validate": "npm ls" | ||
}, | ||
@@ -29,2 +30,3 @@ "repository": {}, | ||
"lint", | ||
"test", | ||
"build" | ||
@@ -48,4 +50,9 @@ ], | ||
}, | ||
"engines": { | ||
"install-node": "5.0.0" | ||
}, | ||
"dependencies": { | ||
"colors": "~1.1.2", | ||
"events": "~1.1.0", | ||
"immutable": "~3.7.5", | ||
"object-assign": "~3.0.0", | ||
@@ -52,0 +59,0 @@ "react": "~0.13.3" |
@@ -19,12 +19,2 @@ import Parser from './Parser' | ||
*/ | ||
parse() { | ||
Parser.prototype.parse.apply(this, arguments) | ||
if (this.ctx.value !== undefined) { | ||
this._res = this.ctx.value | ||
} | ||
return this._res | ||
} | ||
/** | ||
* @override | ||
*/ | ||
token(st) { | ||
@@ -39,2 +29,49 @@ if (st.eatSpace() && st.eol()) { | ||
} | ||
/** | ||
* @param {number} indentSize - default tabSize | ||
* @returns {Promise} | ||
*/ | ||
reformatCode(indentSize) { | ||
const resCode = [] | ||
indentSize = indentSize || this._tabSize | ||
let level = 0 | ||
return this._iter((stream)=> { | ||
const indentNum = stream.indentation() | ||
const str = stream._string.slice(indentNum) | ||
const indentStack = stream.indentStack | ||
if (str !== '') { | ||
stream.readStack(indentStack, (type, substr)=> { | ||
substr = substr.trim('') | ||
switch (type) { | ||
case 'enter': | ||
resCode.push(substr + '\n') | ||
level ++ | ||
_indent() | ||
break | ||
case 'outer': | ||
level -- | ||
resCode.push('\n') | ||
_indent(substr) | ||
break | ||
case 'space': | ||
resCode.push(substr + ' ') | ||
break | ||
case 'align': | ||
resCode.push(substr + '\n') | ||
_indent() | ||
break | ||
default: | ||
resCode.push(substr) | ||
break | ||
} | ||
}) | ||
} | ||
}, {saveStyle: true}).then(()=> { | ||
return resCode.join('') | ||
}) | ||
function _indent(str = '') { | ||
resCode.push(Array(indentSize * level).fill(' ').join('') + str) | ||
} | ||
} | ||
_value(st) { | ||
@@ -45,6 +82,9 @@ const ch = st.trimNext() | ||
case '{': | ||
st.style('operator', 'enter') | ||
return this.pushCtx(this._object, {type: 'object', value: {}}) | ||
case '[': | ||
st.style('operator', 'enter') | ||
return this.pushCtx(this._array, {type: 'array', value: []}) | ||
case '"': | ||
st.style('string') | ||
const str = this._string(st) | ||
@@ -54,2 +94,3 @@ this._saveValue(str) | ||
case '-': | ||
st.style('number') | ||
const num = this._number(st) | ||
@@ -60,5 +101,7 @@ this._saveValue(num) | ||
if (ch >= '0' && ch <= '9') { | ||
st.style('number') | ||
const num2 = this._number(st) | ||
this._saveValue(num2) | ||
} else { | ||
st.style('keyword') | ||
const word = this._word(st) | ||
@@ -71,8 +114,12 @@ this._saveValue(word) | ||
const ctx = this.ctx | ||
if (ctx.type === 'object') { | ||
ctx.value[ctx.key] = value | ||
} else if (ctx.type === 'array') { | ||
ctx.value.push(value) | ||
const type = ctx.get('type') | ||
let val = ctx.get('value') | ||
if (type === 'object') { | ||
val = val.set(ctx.get('key'), value) | ||
this.setCtx('value', val) | ||
} else if (type === 'array') { | ||
val = val.push(value) | ||
this.setCtx('value', val) | ||
} else { | ||
ctx.value = value | ||
this.setCtx('value', value) | ||
} | ||
@@ -83,12 +130,16 @@ } | ||
if (ch === '}') { | ||
st.style('operator', 'outer') | ||
this.popCtx() | ||
this._saveValue(ctx.value) | ||
this._saveValue(ctx.get('value')) | ||
return false | ||
} else if (ch === '"') { | ||
ctx.key = this._string(st) | ||
st.style('property') | ||
this.setCtx('key', this._string(st)) | ||
if (st.trimNext() !== ':') { | ||
return this.error('object syntax error.', st.pos) | ||
} | ||
st.style('operator', 'space') | ||
return this._value(st) | ||
} else if (ch === ',') { | ||
st.style('operator', 'align') | ||
return false | ||
@@ -100,15 +151,17 @@ } | ||
const ch = st.trimNext() | ||
if (ctx.mustEatArrayItem && (ch === ']' || ch === ',')) { | ||
if (ctx.get('mustEatArrayItem') && (ch === ']' || ch === ',')) { | ||
return this.error('array syntax error.', st.pos) | ||
} | ||
if (ch === ']') { | ||
st.style('operator', 'outer') | ||
this.popCtx() | ||
this._saveValue(ctx.value) | ||
this._saveValue(ctx.get('value')) | ||
return false | ||
} else if (ch === ',') { | ||
ctx.mustEatArrayItem = true | ||
st.style('operator', 'align') | ||
this.setCtx('mustEatArrayItem', true) | ||
return false | ||
} | ||
st.backUp(1) | ||
ctx.mustEatArrayItem = false | ||
this.setCtx('mustEatArrayItem', false) | ||
return this._value(st) | ||
@@ -174,5 +227,4 @@ } | ||
// 非数字时后退 | ||
const peek = st.peek() | ||
if (peek && peek.trim()) { | ||
st.backUp() | ||
if (ch) { | ||
st.backUp(1) | ||
} | ||
@@ -179,0 +231,0 @@ if (isNaN(number)) { |
@@ -0,34 +1,23 @@ | ||
import {EventEmitter} from 'events' | ||
import {ENDED_REG} from './constants' | ||
import StringStream from './StringStream' | ||
import assign from 'object-assign' | ||
import Context from './Context' | ||
import {Delayed} from './utils' | ||
import colors from 'colors' | ||
const ERR_LIMIT = 3 | ||
class Context { | ||
constructor(tokenize, data) { | ||
this.pre = null | ||
this.tokenize = tokenize | ||
assign(this, data) | ||
} | ||
push(tokenize, data) { | ||
const ctx = new Context(tokenize, data) | ||
ctx.pre = this | ||
return ctx | ||
} | ||
pop() { | ||
return this.pre | ||
} | ||
} | ||
class Parser { | ||
class Parser extends EventEmitter { | ||
/** | ||
* @param {string} code | ||
* @param {?object} opts | ||
* { | ||
* tabSize: 4 | ||
* } | ||
*/ | ||
constructor(code = '', opts = {}) { | ||
this._code = code.split(ENDED_REG) | ||
super() | ||
this._tabSize = opts.tabSize || 4 | ||
this._res = null | ||
this._row = 0 | ||
this._errorHandlers = [] | ||
this.ctx = new Context() | ||
this._initCode(code) | ||
this._delayed = new Delayed | ||
this._readyPause = false // 触发暂停 | ||
} | ||
@@ -46,10 +35,49 @@ /** | ||
* @param {function} fn | ||
* @param {object} opts | ||
* { | ||
* {number} startRow - 开始遍历的位置, 默认之前停留的时间 | ||
* {boolean} saveStyle - 是否保存样式 | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* } | ||
* @returns {Promise} | ||
* @private | ||
*/ | ||
_iter(fn) { | ||
this._row = 0 | ||
while (this._row < this._code.length) { | ||
if (fn(this._code[this._row])) break | ||
this._row ++ | ||
} | ||
_iter(fn, opts = {}) { | ||
this._row = opts.startRow || this._row | ||
const delayed = opts.delayed ? this._delayed : function(_fn) { _fn() } | ||
return new Promise((res, rej)=> { | ||
if (this._row >= this._code.length) { | ||
return res(true) | ||
} | ||
const errorHandler = (error)=> { | ||
this.removeErrorHandler(errorHandler) | ||
rej(error) | ||
} | ||
this.onError(errorHandler) | ||
const run = ()=> { | ||
if (this._readyPause) { | ||
this._readyPause = false | ||
return | ||
} | ||
const str = this._code[this._row] | ||
const stream = new StringStream(str, {tabSize: this._tabSize, saveStyle: opts.saveStyle}) | ||
while (!stream.eol()) { | ||
// if return true, break the parser | ||
if (this.token(stream)) { | ||
this._res = null | ||
return | ||
} | ||
} | ||
this.emit('parse', fn && fn(stream, this._row), this._row) | ||
this._row ++ | ||
if (this._row < this._code.length) { | ||
delayed(run, opts.delayTime) | ||
} else { | ||
this.removeErrorHandler(errorHandler) | ||
res(true) | ||
} | ||
} | ||
delayed(run, opts.delayTime) | ||
}) | ||
} | ||
@@ -73,36 +101,104 @@ /** | ||
lines = lines.concat(afterLines) | ||
this._errorHandlers.forEach((fn)=> { | ||
fn.call(this, { | ||
message: colors.bgBlack('\n' + lines.join('\n')), | ||
text: msg, | ||
row: row, | ||
col: col, | ||
}) | ||
this.emit('error', { | ||
message: colors.bgBlack('\n' + lines.join('\n')), | ||
text: msg, | ||
row: row, | ||
col: col, | ||
}) | ||
return true | ||
} | ||
clearContext() { | ||
this.ctx = new Context() | ||
this._ctxList.length = 0 | ||
this._row = 0 | ||
} | ||
_initCode(code) { | ||
this._res = null | ||
this._row = 0 | ||
this._code = code.split(ENDED_REG) | ||
this.ctx = new Context() | ||
this._ctxList = [] | ||
} | ||
onParse(fn) { | ||
this.on('parse', fn) | ||
} | ||
removeParseHandler(fn) { | ||
this.removeListener('parse', fn) | ||
} | ||
/** | ||
* @param {function} fn | ||
* @examples | ||
*/ | ||
onError(fn) { | ||
this._errorHandlers.push(fn) | ||
this.on('error', fn) | ||
} | ||
removeErrorHandler(fn) { | ||
this.removeListener('error', fn) | ||
} | ||
/** | ||
* @param {?string} code - 需要解析的代码, 可以为空 | ||
* @param {object} opts | ||
* {number} startRow - 开始遍历的位置, 默认之前停留的时间 | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* @returns {Promise} | ||
*/ | ||
highlight(code, opts = {}) { | ||
code && (this._initCode(code)) | ||
const startRow = opts.startRow || this._row | ||
if (this._ctxList.length !== 0 && opts.startRow > 0) { | ||
// 获取之前的状态 | ||
this.ctx = this._ctxList[startRow - 1] && this._ctxList[startRow - 1].context | ||
this._ctxList = this._ctxList.slice(0, startRow) | ||
} else if (startRow === 0) { | ||
this.clearContext() | ||
} | ||
opts.startRow = startRow | ||
opts.saveStyle = true | ||
return this._iter((stream)=> { | ||
const ctx = { | ||
context: this.ctx, | ||
styleStack: stream.styleStack, | ||
indentStack: stream.indentStack, | ||
} | ||
this._ctxList.push(ctx) | ||
return ctx | ||
}, opts).then(()=> { | ||
this._res = this.ctx.toJS() | ||
this._row = 0 | ||
return this._ctxList.map((item) => item.styleStack) | ||
}) | ||
} | ||
/** | ||
* @param {number} row | ||
* @param {object} opts | ||
* {boolean} delayed - 开启延迟, 用于编辑器分段解析 | ||
* {number} delayTime - 延迟时间, 默认0 | ||
* @returns {Promise} | ||
*/ | ||
highlightFrom(row, opts = {}) { | ||
opts.startRow = row | ||
return this.highlight(null, opts) | ||
} | ||
/** | ||
* 暂停 | ||
*/ | ||
pause() { | ||
this._readyPause = true | ||
} | ||
keepOn(opts) { | ||
return this.highlight(null, opts) | ||
} | ||
/** | ||
* @param {string} code | ||
* @returns {*} parse result | ||
* @returns {Promise} | ||
*/ | ||
parse(code) { | ||
code && (this._code = code.split(ENDED_REG)) | ||
this._iter((str)=> { | ||
const stream = new StringStream(str, {tabSize: this._tabSize}) | ||
while (!stream.eol()) { | ||
// if return true, break the parse | ||
if (this.token(stream)) { | ||
this._res = null | ||
return true | ||
} | ||
} | ||
this.clearContext() | ||
return this._iter(() => {return {context: this.ctx}}).then(()=> { | ||
this._res = this.ctx.toJS() | ||
// clear ctx | ||
this.clearContext() | ||
return this._res | ||
}) | ||
return this._res | ||
} | ||
@@ -116,2 +212,5 @@ pushCtx() { | ||
} | ||
setCtx(key, value) { | ||
this.ctx = this.ctx.set(key, value) | ||
} | ||
/** | ||
@@ -124,4 +223,7 @@ * parse result | ||
} | ||
get row() { | ||
return this._row | ||
} | ||
} | ||
export default Parser |
@@ -9,2 +9,3 @@ import {SPACE_REG} from './constants' | ||
* stringStart: 0, | ||
* saveStyle: false, | ||
* tabSize: 4, | ||
@@ -16,4 +17,7 @@ * } | ||
this._tabSize = opts.tabSize || 4 | ||
this._saveStyle = opts.saveStyle || false | ||
this._pos = 0 | ||
this._string = string | ||
this._styleStack = [] | ||
this._indentStack = [] | ||
} | ||
@@ -139,2 +143,31 @@ /** | ||
} | ||
/** | ||
* @param {string} name - style name, like operator variable | ||
* @param {string} indentCode - 'enter' | 'outer' | 'space' | 'align' | ||
*/ | ||
style(name, indentCode) { | ||
if (this._saveStyle) { | ||
this._styleStack.push(this._pos - 1, name) | ||
indentCode && this._indentStack.push(this._pos - 1, indentCode) | ||
} | ||
} | ||
readStack(stack, fn) { | ||
let pre = 0 | ||
let substr | ||
stack.forEach((pos, key)=> { | ||
if (key % 2 === 0) { | ||
substr = this._string.slice(pre, pos + 1) | ||
pre = pos + 1 | ||
} else { | ||
fn(pos, substr) | ||
} | ||
}) | ||
if (pre < this._string.length - 1) fn(null, this._string.slice(pre)) | ||
} | ||
get styleStack() { | ||
return this._styleStack | ||
} | ||
get indentStack() { | ||
return this._indentStack | ||
} | ||
get pos() { | ||
@@ -141,0 +174,0 @@ return this._pos |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
51717
22
1654
5
+ Addedevents@~1.1.0
+ Addedimmutable@~3.7.5
+ Addedevents@1.1.1(transitive)
+ Addedimmutable@3.7.6(transitive)