6 Star 16 Fork 5

散漫的水元素 / aspects-js

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
ast.js 17.92 KB
一键复制 编辑 原始数据 按行查看 历史
wangmiao 提交于 2020-05-11 12:56 . within and execution
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
const { isClass } = require('./utils');
const SPLITS = {
'(': '',
')': '',
'+': '',
'.': '.',
'?': ''
}
const SPECIAL_TYPE = {
'string': 'String',
'String': 'String',
'number': 'Number',
'Number': 'Number',
'function': 'Function',
'Function': 'Function',
'class': 'Class',
'Class': 'Class',
'RegExp': 'RegExp',
'boolean': 'Boolean',
'Boolean': 'Boolean',
'array': 'Array',
'Array': 'Array',
'object': 'Object',
'Object': 'Object',
'undefined': 'Undefined',
'Undefined': 'Undefined',
'NaN': 'NaN'
}
class ASTError extends Error {
constructor(message) {
super(message);
}
}
const namesByType = type => {
let names = [];
do {
names.push(type.name);
type = type['__proto__'];
} while (type.name);
return names;
}
const namesByObject = object => {
if (typeof object === 'string') return ['String'];
if (typeof object === 'number') {
if (isNaN(object)) return ['NaN', 'Number'];
else return ['Number'];
}
if (typeof object === 'boolean') return ['Boolean'];
if (typeof object === 'undefined') return ['Undefined'];
if (typeof object === 'function') {
if (isClass(object)) return ['Class'];
else return ['Function'];
}
return namesByType(object.constructor);
}
class ASTCompiler {
constructor(exp) {
this.exp = exp;
this.position = 0;
}
nextToken() {
let value = '', position = this.position;
if (this.position < this.exp.length) {
value += this.exp.charAt(this.position);
this.position++;
}
while (this.position < this.exp.length) {
let letter = this.exp.charAt(this.position);
switch (value) {
case '(':
case ')':
case '+':
case ',':
case '..':
case '!':
case '&&':
case '||':
return { value, position };
case '.':
switch (letter) {
case '.': break;
default: return { value, position };
}
break;
case '|':
switch (letter) {
case '|': break;
default: return { value, position };
}
break;
case '&':
switch (letter) {
case '&': break;
default: return { value, position };
}
break;
default:
switch (letter) {
case '(':
case ')':
case '+':
case ',':
case '.':
case '&':
case '|':
case '!':
return { value, position };
}
}
value += letter;
this.position++;
}
return { value, position };
}
compile() {
let token;
let stacks = [];
let operatorStack = [];
let valueStack = [];
stacks.push(operatorStack);
stacks.push(valueStack);
let inCall = false, inProperty = false;
const compute = () => {
let operator = operatorStack.shift();
if (valueStack.length) {
operator.fill(valueStack.shift());
if (operator.completed) {
valueStack.unshift(operator);
} else {
operatorStack.unshift(operator);
}
if (valueStack[0] instanceof ASTCall) {
inCall = false;
} else if (valueStack[0] instanceof ASTProperty) {
inProperty = false;
}
} else {
showError(operator);
throw new ASTError('invalid exp:' + this.exp);
}
}
const unshift = operator => {
//console.debug('unshift', operator.toString());
if (operator.completed) {
valueStack.unshift(operator);
} else {
while (operatorStack.length && operator.priority <= operatorStack[0].priority) {
compute();
}
if (valueStack.length) {
if (!operator.init(valueStack.shift())) {
showError(operator);
throw new ASTError('invalid exp:' + this.exp);
}
}
operatorStack.unshift(operator);
}
}
const showError = (operator) => {
console.error(this.exp);
console.error(new Array(operator.token.position).fill(' ').join('') + '^');
}
while ((token = this.nextToken()).value) {
//console.debug('token', token);
switch (token.value) {
case '+':
unshift(new ASTNameOr(token));
break;
case '(':
if (valueStack.length && valueStack[0] instanceof ASTValue) {
unshift(new ASTCall(token));
inCall = true;
} else {
unshift(new ASTBracket(token));
}
operatorStack = valueStack;
valueStack = [];
stacks.push(valueStack);
break;
case ')':
while (operatorStack.length && !(operatorStack[0] instanceof ASTBracket) && !(operatorStack[0] instanceof ASTCall)) {
compute();
}
//console.debug('stocks---')
//console.debug(stacks.map(stack => stack.join(' , ')).join('\r\n'))
//console.debug('---stocks')
while (operatorStack.length) {
compute();
}
//console.debug('stocks---')
//console.debug(stacks.map(stack => stack.join(' , ')).join('\r\n'))
//console.debug('---stocks')
if (valueStack.length > 1) {
if (valueStack.length) {
showError(valueStack[0]);
//console.debug('error valueStack', valueStack.join(' , '));
}
throw new ASTError('invalid exp:' + this.exp);
} else {
if (valueStack.length === 0 && inCall) {
unshift(new ASTDefaultEllipses(token));
}
operatorStack.unshift(valueStack.shift());
stacks.pop();
valueStack = operatorStack;
operatorStack = stacks[stacks.length - 2];
}
break;
case '.':
unshift(new ASTProperty(token));
inProperty = true;
break;
case ',':
unshift(new ASTComma(token));
break
case '|':
case '||':
unshift(new ASTOr(token));
break;
case '&':
case '&&':
unshift(new ASTAnd(token));
break;
case '!':
unshift(new ASTNot(token));
break;
case '..':
unshift(new ASTEllipses(token));
break;
default:
if (inCall) {
if (SPECIAL_TYPE[token.value]) {
unshift(new ASTSpecialArgument(Object.assign(token, { value: SPECIAL_TYPE[token.value] })));
} else {
unshift(new ASTArgument(token));
}
} else if (inProperty) {
unshift(new ASTFunction(token));
} else {
if (token.value === 'execution') {
unshift(new ASTExecution(token));
} else if (token.value === 'within') {
unshift(new ASTWithin(token));
} else {
unshift(new ASTValue(token));
}
}
}
//console.debug('stocks---')
//console.debug(stacks.map(stack => stack.join(' , ')).join('\r\n'))
//console.debug('---stocks')
}
while (operatorStack.length) {
compute();
}
//console.debug('stocks---')
//console.debug(stacks.map(stack => stack.join(' , ')).join('\r\n'))
//console.debug('---stocks')
if (valueStack.length !== 1) {
if (valueStack.length) {
showError(valueStack[0]);
//console.debug('error valueStack', valueStack.join(' , '));
}
throw new ASTError('invalid exp:' + this.exp);
} else {
//console.debug(valueStack[0]);
return valueStack[0];
}
}
}
const PRIORITIES = [',', '..', '|', '&', '!', '.', '+', 'keyword', '()'];
class ASTOperator {
constructor(token) {
this.token = token;
this[PROPERTY_VARIABLES] = new Array(this.count).fill(false).map(_ => NONE_OPERATOR);
}
fill(operator) {
for (let i = this.count - 1; i >= 0; i--) {
if (this[PROPERTY_VARIABLES][i] === NONE_OPERATOR) {
this[PROPERTY_VARIABLES][i] = operator;
return true;
}
}
return false;
}
init(operator) {
if (this[PROPERTY_VARIABLES][0] === NONE_OPERATOR) {
this[PROPERTY_VARIABLES][0] = operator;
return true;
} else {
return false;
}
}
get completed() {
return this[PROPERTY_VARIABLES].every(v => v !== NONE_OPERATOR);
}
get variables() {
return this[PROPERTY_VARIABLES];
}
get count() {
return 1;
}
get priority() {
return 0;
}
execute(context) {
return false;
}
}
class ASTNoneOperator extends ASTOperator {
constructor(token) {
super(token);
}
get count() {
return 0;
}
toString() {
return '#'
}
execute(context) {
return false;
}
}
class ASTEllipses extends ASTOperator {
constructor(token) {
super(token);
}
get completed() {
return true;
}
get count() {
return 0;
}
get priority() {
return PRIORITIES.indexOf('..');
}
toString() {
return `..`;
}
execute(context) {
return true;
}
}
class ASTDefaultEllipses extends ASTEllipses {
toString() {
return ``;
}
}
class ASTUnaryOperator extends ASTOperator {
constructor(token) {
super(token);
}
get value() {
return this.variables[0];
}
get count() {
return 1;
}
execute(context) {
return false;
}
}
class ASTValue extends ASTUnaryOperator {
constructor(token) {
super(token);
this.fill(token.value);
if (token.value.startsWith('?')) {
this.reg = new RegExp('^[_\\w]' + token.value.substring(1).replace(/\?/ig, '[_\\w\\d]').replace(/\*/ig, '[_\\w\\d]*') + '$', 'g');
} else if (token.value.startsWith('*')) {
this.reg = new RegExp('^([_\\w]?|[_\\w][_\\w\\d]*)' + token.value.substring(1).replace(/\?/ig, '[_\\w\\d]').replace(/\*/ig, '[_\\w\\d]*') + '$', 'g');
} else {
this.reg = new RegExp('^' + token.value.replace(/\?/ig, '[_\\w\\d]').replace(/\*/ig, '[_\\w\\d]*') + '$', 'g');
}
}
get priority() {
return PRIORITIES.indexOf('()');
}
toString() {
return this.value;
}
inner(context) {
if (context instanceof String || typeof context === 'string') {
this.reg.lastIndex = 0;
return this.reg.test(context);
} else if (context instanceof Array) {
return context.some(name => {
this.reg.lastIndex = 0;
return this.reg.test(name)
});
} else {
this.reg.lastIndex = 0;
return this.reg.test(context.fun.name);
}
}
execute(context) {
let result = this.inner(context);
//console.debug('value check', this.reg, context, result);
return result;
}
}
class ASTFunction extends ASTValue {
constructor(token) {
super(token);
}
execute(context) {
return super.execute(context.fun.name);
}
}
class ASTArgument extends ASTValue {
constructor(token) {
super(token);
}
execute(context) {
if (context.length !== 1) return false;
let result = super.execute(namesByObject(context[0]));
//console.debug('execute arg', this.reg, namesByObject(context[0]).join('|'), result);
return result;
}
}
class ASTSpecialArgument extends ASTValue {
constructor(token) {
super(token);
this.special = token.value;
}
execute(context) {
if (context.length !== 1) return false;
let types = namesByObject(context[0]);
let result = types.includes(this.special);
//console.debug('execute special arg', this.special, namesByObject(context[0]).join('|'), result);
return result;
}
}
class ASTBracket extends ASTUnaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('()');
}
toString() {
return `(${this.value})`;
}
execute(context) {
return this.value.execute(context);
}
}
class ASTNot extends ASTUnaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('!');
}
toString() {
return `!${this.value}`;
}
execute(context) {
return !this.value.execute(context);
}
}
class ASTExecution extends ASTUnaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('keyword');
}
toString() {
return `execution${this.value}`;
}
execute(context) {
return this.value.execute(context);
}
}
class ASTWithin extends ASTUnaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('keyword');
}
toString() {
return `within${this.value}`;
}
execute(context) {
return this.value.execute(namesByType(context.type));
}
}
class ASTBinaryOperator extends ASTOperator {
constructor(token) {
super(token);
}
get left() {
return this.variables[0];
}
get right() {
return this.variables[1];
}
get count() {
return 2;
}
}
class ASTNameOr extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('+');
}
toString() {
return `${this.left}+${this.right}`;
}
execute(context) {
return this.left.execute(context) || this.right.execute(context);
}
}
class ASTComma extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf(',');
}
toString() {
return `${this.left},${this.right}`;
}
execute(context) {
if (context instanceof Array) {
for (let i = 0; i <= context.length; i++) {
let leftResult = this.left.execute(context.slice(0, i));
let rightResult = this.right.execute(context.slice(i));
//console.debug(this.toString(), i, context.slice(0, i), context.slice(i), leftResult, rightResult);
if (leftResult && rightResult) return true;
}
return false;
} else {
if (typeof context.args === 'undefined') {
return true;
} else {
return this.execute(context.args);
}
}
}
}
class ASTProperty extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('.');
}
toString() {
return `${this.left}.${this.right}`;
}
execute(context) {
return this.left.execute(namesByType(context.type)) && this.right.execute(context);
}
}
class ASTCall extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('()');
}
toString() {
return `${this.left}(${this.right})`;
}
execute(context) {
return this.left.execute(context) && this.right.execute(context.args);
}
}
class ASTOr extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('|');
}
toString() {
return `${this.left}|${this.right}`;
}
execute(context) {
return this.left.execute(context) || this.right.execute(context);
}
}
class ASTAnd extends ASTBinaryOperator {
constructor(token) {
super(token);
}
get priority() {
return PRIORITIES.indexOf('&');
}
toString() {
return `${this.left}&${this.right}`;
}
execute(context) {
return this.left.execute(context) && this.right.execute(context);
}
}
class AST {
static compile(exp) {
return new ASTCompiler(exp).compile();
}
}
const PROPERTY_VARIABLES = Symbol.for("variables");
const NONE_OPERATOR = new ASTNoneOperator();
module.exports = {
AST,
ASTError
}
JavaScript
1
https://gitee.com/wm123450405/aspects-js.git
git@gitee.com:wm123450405/aspects-js.git
wm123450405
aspects-js
aspects-js
master

搜索帮助