diff --git a/package.json b/package.json
index da5195d..f722969 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "homunculus",
- "version": "0.1.8",
+ "version": "0.1.9",
"description": "A lexer&parser by Javascript",
"maintainers": [
{
diff --git a/src/parser/es6/Node.js b/src/parser/es6/Node.js
new file mode 100644
index 0000000..a5cc46d
--- /dev/null
+++ b/src/parser/es6/Node.js
@@ -0,0 +1,163 @@
+var Class = require('../../util/Class');
+var Node = Class(function(type, children) {
+ this.type = type;
+ if(type == Node.TOKEN) {
+ this.children = children;
+ }
+ else if(Array.isArray(children)) {
+ this.children = children;
+ }
+ else {
+ this.children = children ? [children] : [];
+ }
+ this.p = null;
+ this.pr = null;
+ this.ne = null;
+ return this;
+}).methods({
+ name: function() {
+ return this.type;
+ },
+ leaves: function() {
+ return this.children;
+ },
+ leaf: function(i) {
+ return this.children[i];
+ },
+ number: function() {
+ return this.children.length;
+ },
+ add: function() {
+ var self = this;
+ var args = Array.prototype.slice.call(arguments, 0);
+ args.forEach(function(node) {
+ node.parent(self);
+ var last = self.children[self.children.length - 1];
+ if(last) {
+ last.next(node);
+ node.prev(last);
+ }
+ self.children.push(node);
+ });
+ return self;
+ },
+ token: function() {
+ return this.children;
+ },
+ parent: function(p) {
+ if(p) {
+ this.p = p;
+ }
+ return this.p;
+ },
+ prev: function(pr) {
+ if(pr) {
+ this.pr = pr;
+ }
+ return this.pr;
+ },
+ next: function(ne) {
+ if(ne) {
+ this.ne = ne;
+ }
+ return this.ne;
+ }
+}).statics({
+ PROGRAM: 'program',
+ ELEMS: 'elems',
+ ELEM: 'elem',
+ CSTSTMT: 'cststmt',
+ LETSTMT: 'letstmt',
+ VARSTMT: 'varstmt',
+ VARDECL: 'vardecl',
+ FNBODY: 'fnbody',
+ BLOCK: 'block',
+ ITERSTMT: 'iterstmt',
+ TOKEN: 'token',
+ FNPARAMS: 'fnparams',
+ BINDELEMENT: 'bindelement',
+ RESTPARAM: 'restparam',
+ EXPR: 'expr',
+ CLASSDECL: 'classdecl',
+ CLASSTAIL: 'classtail',
+ HERITAGE: 'heritage',
+ CLASSBODY: 'classbody',
+ METHOD: 'method',
+ SUPERSTMT: 'superstmt',
+ GETFN: 'getfn',
+ SETFN: 'setfn',
+ PROGRAM: 'program',
+ STMT: 'stmt',
+ ASSIGN: 'assign',
+ EMPTSTMT: 'emptstmt',
+ IFSTMT: 'ifstmt',
+ CNTNSTMT: 'cntnstmt',
+ BRKSTMT: 'brkstmt',
+ RETSTMT: 'retstmt',
+ WITHSTMT: 'withstmt',
+ SWCHSTMT: 'swchstmt',
+ CASEBLOCK: 'caseblock',
+ CASECLAUSE: 'caseclause',
+ DFTCLAUSE: 'dftclause',
+ LABSTMT: 'labstmt',
+ THRSTMT: 'thrstmt',
+ TRYSTMT: 'trystmt',
+ DEBSTMT: 'debstmt',
+ EXPRSTMT: 'exprstmt',
+ CACH: 'cach',
+ FINL: 'finl',
+ FNDECL: 'fndecl',
+ FNEXPR: 'fnexpr',
+ ASSIGNEXPR: 'assignexpr',
+ CNDTEXPR: 'cndtexpr',
+ LOGOREXPR: 'logorexpr',
+ LOGANDEXPR: 'logandexpr',
+ BITOREXPR: 'bitorexpr',
+ BITANDEXPR: 'bitandexpr',
+ BITXOREXPR: 'bitxorexpr',
+ EQEXPR: 'eqexpr',
+ RELTEXPR: 'reltexpr',
+ SHIFTEXPR: 'shiftexpr',
+ ADDEXPR: 'addexpr',
+ MTPLEXPR: 'mtplexpr',
+ UNARYEXPR: 'unaryexpr',
+ MMBEXPR: 'mmbexpr',
+ PRMREXPR: 'prmrexpr',
+ ARRLTR: 'arrltr',
+ OBJLTR: 'objltr',
+ PROPTASSIGN: 'proptassign',
+ PROPTNAME: 'proptname',
+ PROPTSETS: 'propsets',
+ ARGS: 'args',
+ ARGLIST: 'arglist',
+ IMPTSTMT: 'imptstmt',
+ POSTFIXEXPR: 'postfixexpr',
+ NEWEXPR: 'newexpr',
+ CALLEXPR: 'callexpr',
+ ARRBINDPAT: 'arrbindpat',
+ OBJBINDPAT: 'objbindpat',
+ BINDPROPT: 'bindpropt',
+ SINGLENAME: 'singlename',
+ BINDELEM: 'bindelem',
+ BINDREST: 'bindrest',
+ BINDID: 'bindid',
+ SPREAD: 'spread',
+ ARRCMPH: 'arrcmph',
+ CMPHFOR: 'cmphfor',
+ getKey: function(s) {
+ if(!s) {
+ throw new Error('empty value');
+ }
+ if(!keys) {
+ var self = this;
+ keys = {};
+ Object.keys(this).forEach(function(k) {
+ var v = self[k];
+ keys[v] = k;
+ });
+ }
+ return keys[s];
+ }
+});
+var keys;
+module.exports = Node;
\ No newline at end of file
diff --git a/src/parser/es6/Parser.js b/src/parser/es6/Parser.js
new file mode 100644
index 0000000..148c086
--- /dev/null
+++ b/src/parser/es6/Parser.js
@@ -0,0 +1,1534 @@
+var Class = require('../../util/Class');
+var character = require('../../util/character');
+var Lexer = require('../../lexer/Lexer');
+var Rule = require('../../lexer/rule/EcmascriptRule');
+var Token = require('../../lexer/Token');
+var Node = require('./Node');
+var S = {};
+S[Token.BLANK] = S[Token.TAB] = S[Token.COMMENT] = S[Token.LINE] = S[Token.ENTER] = true;
+var Parser = Class(function(lexer) {
+ this.init(lexer);
+}).methods({
+ parse: function(code) {
+ this.lexer.parse(code);
+ this.tree = this.program();
+ return this.tree;
+ },
+ ast: function() {
+ return this.tree;
+ },
+ init: function(lexer) {
+ this.look = null;
+ this.tokens = null;
+ this.lastLine = 1;
+ this.lastCol = 1;
+ this.line = 1;
+ this.col = 1;
+ this.index = 0;
+ this.length = 0;
+ this.ignores = {};
+ this.hasMoveLine = false;
+ this.tree = {};
+ if(lexer) {
+ this.lexer = lexer;
+ }
+ else if(this.lexer) {
+ this.lexer.init();
+ }
+ else {
+ this.lexer = new Lexer(new Rule());
+ }
+ },
+ program: function() {
+ this.tokens = this.lexer.tokens();
+ this.length = this.tokens.length;
+ if(this.tokens.length) {
+ this.move();
+ }
+ var node = new Node(Node.PROGRAM);
+ while(this.look) {
+ node.add(this.element());
+ }
+ return node;
+ },
+ element: function(allowSuper) {
+ if(this.look.content() == 'function') {
+ return this.fndecl();
+ }
+ else if(this.look.content() == 'class') {
+ return this.classdecl();
+ }
+ else {
+ return this.stmt(allowSuper);
+ }
+ },
+ stmt: function(allowSuper) {
+ if(!this.look) {
+ this.error();
+ }
+ switch(this.look.content()) {
+ case 'let':
+ return this.letstmt();
+ case 'const':
+ return this.cststmt();
+ case 'var':
+ return this.varstmt();
+ case '{':
+ return this.block();
+ case ';':
+ return this.emptstmt();
+ case 'if':
+ return this.ifstmt();
+ case 'do':
+ case 'while':
+ case 'for':
+ return this.iterstmt();
+ case 'continue':
+ return this.cntnstmt();
+ case 'break':
+ return this.brkstmt();
+ case 'return':
+ return this.retstmt();
+ case 'with':
+ return this.withstmt();
+ case 'switch':
+ return this.swchstmt();
+ case 'throw':
+ return this.thrstmt();
+ case 'try':
+ return this.trystmt();
+ case 'debugger':
+ return this.debstmt();
+ case 'super':
+ if(!allowSuper) {
+ this.error('super must in a class');
+ }
+ return this.superstmt();
+ case 'import':
+ return this.imptstmt();
+ default:
+ if(this.look.type() == Token.ID) {
+ for(var i = this.index; i < this.length; i++) {
+ var token = this.tokens[i];
+ if(!S[token.type()]) {
+ if(token.content() == ':') {
+ return this.labstmt();
+ }
+ else {
+ return this.exprstmt();
+ }
+ }
+ }
+ }
+ return this.exprstmt();
+ }
+ },
+ exprstmt: function() {
+ var node = new Node(Node.EXPRSTMT);
+ node.add(this.expr(), this.match(';'));
+ return node;
+ },
+ cststmt: function(noSem) {
+ var node = new Node(Node.CSTSTMT);
+ node.add(
+ this.match('const'),
+ this.vardecl()
+ );
+ while(this.look && this.look.content() == ',') {
+ node.add(
+ this.match(),
+ this.vardecl()
+ );
+ }
+ if(!noSem) {
+ node.add(this.match(';'));
+ }
+ return node;
+ },
+ letstmt: function(noSem) {
+ var node = new Node(Node.LETSTMT);
+ node.add(
+ this.match('let'),
+ this.vardecl()
+ );
+ while(this.look && this.look.content() == ',') {
+ node.add(
+ this.match(),
+ this.vardecl()
+ );
+ }
+ if(!noSem) {
+ node.add(this.match(';'));
+ }
+ return node;
+ },
+ varstmt: function(noSem) {
+ var node = new Node(Node.VARSTMT);
+ node.add(
+ this.match('var'),
+ this.vardecl()
+ );
+ while(this.look && this.look.content() == ',') {
+ node.add(
+ this.match(),
+ this.vardecl()
+ );
+ }
+ if(!noSem) {
+ node.add(this.match(';'));
+ }
+ return node;
+ },
+ vardecl: function() {
+ var node = new Node(Node.VARDECL);
+ if(!this.look) {
+ this.error('missing variable name');
+ }
+ if(['[', '{'].indexOf(this.look.content()) > -1) {
+ node.add(this.bindpat());
+ if(!this.look || this.look.content() != '=') {
+ this.error('missing = in destructuring declaration');
+ }
+ node.add(this.assign());
+ }
+ else {
+ node.add(this.match(Token.ID, 'missing variable name'));
+ if(this.look && this.look.content() == '=') {
+ node.add(this.assign());
+ }
+ }
+ return node;
+ },
+ bindpat: function() {
+ if(this.look.content() == '[') {
+ return this.arrbindpat();
+ }
+ else if(this.look.content() == '{') {
+ return this.objbindpat();
+ }
+ },
+ arrbindpat: function() {
+ var node = new Node(Node.ARRBINDPAT);
+ node.add(this.match('['));
+ while(this.look && this.look.content() != ']') {
+ if(this.look.content() == ',') {
+ node.add(this.match());
+ }
+ else if(this.look.content() == '...') {
+ break;
+ }
+ else {
+ node.add(this.bindelem());
+ }
+ }
+ if(this.look.content() == '...') {
+ node.add(this.restparam());
+ }
+ node.add(this.match(']', 'missing ] after element list'));
+ return node;
+ },
+ bindelem: function() {
+ var node = new Node(Node.BINDELEM);
+ if(['[', '{'].indexOf(this.look.content()) > -1) {
+ node.add(this.bindpat());
+ if(this.look && this.look.content() == '=') {
+ node.add(this.assign());
+ }
+ }
+ else {
+ return this.singlename();
+ }
+ return node;
+ },
+ singlename: function() {
+ var node = new Node(Node.SINGLENAME);
+ node.add(this.match(Token.ID));
+ if(this.look && this.look.content() == '=') {
+ node.add(this.assign());
+ }
+ return node;
+ },
+ objbindpat: function() {
+ var node = new Node(Node.OBJBINDPAT);
+ node.add(this.match('{'));
+ while(this.look && this.look.content() != '}') {
+ node.add(this.bindpropt());
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
+ }
+ }
+ node.add(this.match('}', 'missing } after property list'));
+ return node;
+ },
+ bindpropt: function() {
+ var node = new Node(Node.BINDPROPT);
+ switch(this.look.type()) {
+ case Token.ID:
+ case Token.STRING:
+ case Token.NUMBER:
+ break;
+ default:
+ this.error('invalid property id');
+ }
+ //根据LL2分辨是PropertyName[?Yield, ?GeneratorParameter] : BindingElement[?Yield, ?GeneratorParameter]
+ //还是SingleNameBinding [?Yield, ?GeneratorParameter]
+ for(var i = this.index; i < this.length; i++) {
+ var next = this.tokens[i];
+ if(!S[next.tag()]) {
+ if(next.content() == ':') {
+ node.add(this.match(), this.match());
+ node.add(this.bindelem());
+ }
+ else {
+ node.add(this.singlename());
+ }
+ return node;
+ }
+ }
+ this.error('missing : after property id');
+ },
+ assign: function() {
+ var node = new Node(Node.ASSIGN);
+ node.add(this.match('='));
+ if(!this.look) {
+ this.error();
+ }
+ node.add(this.assignexpr());
+ return node;
+ },
+ block: function() {
+ var node = new Node(Node.BLOCK);
+ node.add(this.match('{'));
+ while(this.look && this.look.content() != '}') {
+ node.add(this.stmt());
+ }
+ node.add(this.match('}', 'missing } in compound statement'));
+ return node;
+ },
+ emptstmt: function() {
+ var node = new Node(Node.EMPTSTMT);
+ node.add(this.match(';'));
+ return node;
+ },
+ ifstmt: function() {
+ var node = new Node(Node.IFSTMT);
+ node.add(
+ this.match('if'),
+ this.match('('),
+ this.expr(),
+ this.match(')'),
+ this.stmt()
+ );
+ if(this.look && this.look.content() == 'else') {
+ node.add(
+ this.match('else'),
+ this.stmt()
+ );
+ }
+ return node;
+ },
+ iterstmt: function() {
+ var node = new Node(Node.ITERSTMT);
+ switch(this.look.content()) {
+ case 'do':
+ node.add(
+ this.match(),
+ this.stmt(),
+ this.match('while'),
+ this.match('('),
+ this.expr(),
+ this.match(')'),
+ this.match(';')
+ );
+ break;
+ case 'while':
+ node.add(
+ this.match(),
+ this.match('('),
+ this.expr(),
+ this.match(')'),
+ this.stmt()
+ );
+ break;
+ case 'for':
+ node.add(
+ this.match(),
+ this.match('(')
+ );
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == 'var' || this.look.content() == 'let') {
+ var node2 = this.look.content() == 'var' ? this.varstmt(true) : this.letstmt(true);
+ if(!this.look) {
+ this.error('missing ; after for-loop initializer');
+ }
+ if(this.look.content() == 'in') {
+ if(node2.leaves().length > 2) {
+ this.error('invalid for/in left-hand side');
+ }
+ node.add(node2);
+ node.add(
+ this.match(),
+ this.expr()
+ );
+ }
+ else {
+ node.add(node2);
+ node.add(this.match(';'));
+ if(this.look.content() != ';') {
+ node.add(this.expr());
+ }
+ node.add(this.match(';'));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() != ')') {
+ node.add(this.expr());
+ }
+ }
+ }
+ else {
+ if(this.look.content() == 'in') {
+ this.error();
+ }
+ var hasIn = false;
+ for(var i = this.index; i < this.length; i++) {
+ var t = this.tokens[i];
+ if(t.content() == 'in') {
+ hasIn = true;
+ break;
+ }
+ else if(t.content() == ')') {
+ break;
+ }
+ }
+ if(hasIn) {
+ node.add(this.expr(true), this.match('in'), this.expr());
+ }
+ else {
+ if(this.look.content() != ';') {
+ node.add(this.expr());
+ }
+ //for的;不能省略,强制判断
+ if(!this.look || this.look.content() != ';') {
+ this.error('missing ;')
+ }
+ node.add(this.match(';'));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() != ';') {
+ node.add(this.expr());
+ }
+ if(!this.look || this.look.content() != ';') {
+ this.error('missing ;')
+ }
+ node.add(this.match(';'));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() != ')') {
+ node.add(this.expr());
+ }
+ }
+ }
+ node.add(this.match(')'));
+ node.add(this.stmt());
+ }
+ return node;
+ },
+ cntnstmt: function() {
+ var node = new Node(Node.CNTNSTMT);
+ node.add(this.match('continue', true));
+ if(this.look && this.look.type() == Token.ID) {
+ node.add(this.match());
+ }
+ node.add(this.match(';'));
+ return node;
+ },
+ brkstmt: function() {
+ var node = new Node(Node.BRKSTMT);
+ node.add(this.match('break', true));
+ if(this.look && this.look.type() == Token.ID) {
+ node.add(this.match());
+ }
+ node.add(this.match(';'));
+ return node;
+ },
+ retstmt: function() {
+ var node = new Node(Node.RETSTMT);
+ node.add(this.match('return', true));
+ //return后换行视作省略;,包括多行注释的换行
+ if(this.look) {
+ if(this.look.content() == ';'
+ || this.look.content() == '}'
+ || this.look.type() == Token.LINE
+ || this.look.type() == Token.COMMENT) {
+ node.add(this.match(';'));
+ }
+ else {
+ node.add(this.expr(), this.match(';'));
+ }
+ }
+ else {
+ node.add(this.match(';'));
+ }
+ return node;
+ },
+ withstmt: function() {
+ var node = new Node(Node.WITHSTMT);
+ node.add(
+ this.match('with'),
+ this.match('('),
+ this.expr(),
+ this.match(')'),
+ this.stmt()
+ );
+ return node;
+ },
+ swchstmt: function() {
+ var node = new Node(Node.SWCHSTMT);
+ node.add(
+ this.match('switch'),
+ this.match('('),
+ this.expr(),
+ this.match(')'),
+ this.caseblock()
+ );
+ return node;
+ },
+ caseblock: function() {
+ var node = new Node(Node.CASEBLOCK);
+ node.add(this.match('{'));
+ while(this.look && this.look.content() != '}') {
+ if(this.look.content() == 'case') {
+ node.add(this.caseclause());
+ }
+ else if(this.look.content() == 'default') {
+ node.add(this.dftclause());
+ }
+ else {
+ this.error('invalid switch statement');
+ }
+ }
+ node.add(this.match('}'));
+ return node;
+ },
+ caseclause: function() {
+ var node = new Node(Node.CASECLAUSE);
+ node.add(
+ this.match('case'),
+ this.expr(),
+ this.match(':')
+ );
+ while(this.look
+ && this.look.content() != 'case'
+ && this.look.content() != 'default'
+ && this.look.content() != '}') {
+ node.add(this.stmt());
+ }
+ return node;
+ },
+ dftclause: function() {
+ var node = new Node(Node.DFTCLAUSE);
+ node.add(
+ this.match('default'),
+ this.match(':')
+ );
+ while(this.look && this.look.content() != '}') {
+ node.add(this.stmt());
+ }
+ return node;
+ },
+ labstmt: function() {
+ var node = new Node(Node.LABSTMT);
+ node.add(
+ this.match(Token.ID),
+ this.match(':'),
+ this.stmt()
+ );
+ return node;
+ },
+ thrstmt: function() {
+ var node = new Node(Node.THRSTMT);
+ node.add(
+ this.match('throw', true),
+ this.expr(),
+ this.match(';')
+ );
+ return node;
+ },
+ trystmt: function() {
+ var node = new Node(Node.TRYSTMT);
+ node.add(
+ this.match('try'),
+ this.block()
+ );
+ if(this.look && this.look.content() == 'catch') {
+ node.add(this.cach());
+ if(this.look && this.look.content() == 'finally') {
+ node.add(this.finl());
+ }
+ }
+ else {
+ node.add(this.finl());
+ }
+ return node;
+ },
+ debstmt: function() {
+ var node = new Node(Node.DEBSTMT);
+ node.add(this.match('debugger'), this.match(';'));
+ return node;
+ },
+ cach: function() {
+ var node = new Node(Node.CACH);
+ node.add(
+ this.match('catch'),
+ this.match('('),
+ this.match(Token.ID, 'missing identifier in catch'),
+ this.match(')'),
+ this.block()
+ );
+ return node;
+ },
+ finl: function() {
+ var node = new Node(Node.FINL);
+ node.add(
+ this.match('finally'),
+ this.block()
+ );
+ return node;
+ },
+ superstmt: function() {
+ var node = new Node(Node.SUPERSTMT);
+ node.add(this.match('super'));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == '.') {
+ while(this.look && this.look.content() == '.') {
+ node.add(this.match());
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == 'super') {
+ node.add(this.match());
+ }
+ else {
+ break;
+ }
+ }
+ if(this.look.content() != '(') {
+ node.add(this.match(Token.ID));
+ while(this.look && this.look.content() == '.') {
+ node.add(this.match(), this.match(Token.ID));
+ }
+ }
+ }
+ node.add(
+ this.args(),
+ this.match(';')
+ );
+ return node;
+ },
+ imptstmt: function() {
+ var node = new Node(Node.IMPTSTMT);
+ return node;
+ },
+ fndecl: function() {
+ var node = new Node(Node.FNDECL);
+ node.add(
+ this.match('function'),
+ this.match(Token.ID, 'function statement requires a name'),
+ this.match('(')
+ );
+ if(!this.look) {
+ this.error('missing formal parameter');
+ }
+ if(this.look.content() != ')') {
+ node.add(this.fnparams());
+ }
+ node.add(
+ this.match(')'),
+ this.match('{'),
+ this.fnbody(),
+ this.match('}')
+ );
+ return node;
+ },
+ fnexpr: function() {
+ var node = new Node(Node.FNEXPR);
+ node.add(this.match('function'));
+ if(!this.look) {
+ this.error('missing formal parameter');
+ }
+ if(this.look.type() == Token.ID) {
+ node.add(this.match());
+ }
+ node.add(this.match('('));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() != ')') {
+ node.add(this.fnparams());
+ }
+ node.add(
+ this.match(')'),
+ this.match('{'),
+ this.fnbody(),
+ this.match('}', 'missing } in compound statement')
+ );
+ return node;
+ },
+ fnparams: function() {
+ var node = new Node(Node.FNPARAMS);
+ while(this.look && this.look.content() != ')' && this.look.content() != '...') {
+ node.add(this.match(Token.ID, 'missing formal parameter'));
+ if(this.look) {
+ if(this.look.content() == ',') {
+ node.add(this.match());
+ }
+ else if(this.look.content() == '=') {
+ node.add(this.bindelement());
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
+ }
+ }
+ }
+ }
+ if(!this.look) {
+ this.error('missing ) after formal parameters');
+ }
+ if(this.look.content() == '...') {
+ node.add(this.restparam());
+ }
+ return node;
+ },
+ bindelement: function() {
+ var node = new Node(Node.BINDELEMENT);
+ node.add(this.match('='), this.assignexpr());
+ return node;
+ },
+ restparam: function() {
+ var node = new Node(Node.RESTPARAM);
+ node.add(this.match('...'), this.match(Token.ID));
+ return node;
+ },
+ fnbody: function(allowSuper) {
+ var node = new Node(Node.FNBODY);
+ while(this.look && this.look.content() != '}') {
+ node.add(this.element(allowSuper));
+ }
+ return node;
+ },
+ classdecl: function() {
+ var node = new Node(Node.CLASSDECL);
+ node.add(this.match('class'), this.match(Token.ID));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == 'extends') {
+ node.add(this.heratige());
+ }
+ node.add(
+ this.match('{'),
+ this.classbody(),
+ this.match('}')
+ );
+ return node;
+ },
+ heratige: function() {
+ var node = new Node(Node.HERITAGE);
+ node.add(this.match('extends'), this.match(Token.ID));
+ return node;
+ },
+ classbody: function() {
+ var node = new Node(Node.CLASSBODY),
+ methods = {},
+ hasStatic = false;
+ while(this.look && this.look.content() != '}') {
+ if(this.look.content() == ';') {
+ node.add(this.match());
+ continue;
+ }
+ hasStatic = false;
+ if(this.look.content() == 'static') {
+ node.add(this.match());
+ hasStatic = true;
+ }
+ if(!this.look) {
+ this.error();
+ }
+ node.add(this.method(hasStatic, methods));
+ }
+ return node;
+ },
+ method: function(hasStatic, methods, statics) {
+ var node = new Node(Node.METHOD);
+ if(this.look.content() == 'get') {
+ node.add(this.match(), this.getfn());
+ }
+ else if(this.look.content() == 'set') {
+ node.add(this.match(), this.setfn());
+ }
+ else {
+ node.add(this.match(Token.ID));
+ var id = node.leaves()[0].token().content();
+ if(methods.hasOwnProperty(id)) {
+ this.error('duplicate method decl in class');
+ }
+ methods[id] = true;
+ node.add(this.match('('));
+ if(this.look.content() != ')') {
+ node.add(this.fnparams());
+ }
+ node.add(
+ this.match(')'),
+ this.match('{'),
+ this.fnbody(true),
+ this.match('}', 'missing } in compound statement')
+ );
+ }
+ return node;
+ },
+ expr: function(noIn) {
+ var node = new Node(Node.EXPR),
+ assignexpr = this.assignexpr(noIn);
+ if(this.look && this.look.content() == ',') {
+ node.add(assignexpr);
+ while(this.look && this.look.content() == ',') {
+ node.add(this.match(), this.assignexpr(noIn));
+ }
+ }
+ else {
+ return assignexpr;
+ }
+ return node;
+ },
+ assignexpr: function(noIn) {
+ var node = new Node(Node.ASSIGNEXPR),
+ cndt = this.cndtexpr(noIn);
+ if(this.look && {
+ '*=': true,
+ '/=': true,
+ '%=': true,
+ '+=': true,
+ '-=': true,
+ '<<=': true,
+ '>>=': true,
+ '>>>=': true,
+ '&=': true,
+ '^=': true,
+ '|=': true,
+ '=': true
+ }.hasOwnProperty(this.look.content())) {
+ node.add(cndt, this.match(), this.assignexpr(noIn));
+ }
+ else {
+ return cndt;
+ }
+ return node;
+ },
+ cndtexpr: function(noIn) {
+ var node = new Node(Node.CNDTEXPR),
+ logorexpr = this.logorexpr(noIn);
+ if(this.look && this.look.content() == '?') {
+ node.add(
+ logorexpr,
+ this.match(),
+ this.assignexpr(noIn),
+ this.match(':'),
+ this.assignexpr(noIn)
+ );
+ }
+ else {
+ return logorexpr;
+ }
+ return node;
+ },
+ logorexpr: function(noIn) {
+ var node = new Node(Node.LOGOREXPR),
+ logandexpr = this.logandexpr(noIn);
+ if(this.look && this.look.content() == '||') {
+ node.add(logandexpr);
+ while(this.look && this.look.content() == '||') {
+ node.add(
+ this.match(),
+ this.logandexpr(noIn)
+ );
+ }
+ }
+ else {
+ return logandexpr;
+ }
+ return node;
+ },
+ logandexpr: function(noIn) {
+ var node = new Node(Node.LOGANDEXPR),
+ bitorexpr = this.bitorexpr(noIn);
+ if(this.look && this.look.content() == '&&') {
+ node.add(bitorexpr);
+ while(this.look && this.look.content() == '&&') {
+ node.add(
+ this.match(),
+ this.bitorexpr(noIn)
+ );
+ }
+ }
+ else {
+ return bitorexpr;
+ }
+ return node;
+ },
+ bitorexpr: function(noIn) {
+ var node = new Node(Node.BITOREXPR),
+ bitxorexpr = this.bitxorexpr(noIn);
+ if(this.look && this.look.content() == '|') {
+ node.add(bitxorexpr);
+ while(this.look && this.look.content() == '|') {
+ node.add(
+ this.match(),
+ this.bitxorexpr(noIn)
+ );
+ }
+ }
+ else {
+ return bitxorexpr;
+ }
+ return node;
+ },
+ bitxorexpr: function(noIn) {
+ var node = new Node(Node.BITXOREXPR),
+ bitandexpr = this.bitandexpr(noIn);
+ if(this.look && this.look.content() == '^') {
+ node.add(bitandexpr);
+ while(this.look && this.look.content() == '^') {
+ node.add(
+ this.match(),
+ this.bitandexpr(noIn)
+ );
+ }
+ }
+ else {
+ return bitandexpr;
+ }
+ return node;
+ },
+ bitandexpr: function(noIn) {
+ var node = new Node(Node.BITANDEXPR),
+ eqexpr = this.eqexpr(noIn);
+ if(this.look && this.look.content() == '&') {
+ node.add(eqexpr);
+ while(this.look && this.look.content() == '&') {
+ node.add(
+ this.match(),
+ this.eqexpr(noIn)
+ );
+ }
+ }
+ else {
+ return eqexpr;
+ }
+ return node;
+ },
+ eqexpr: function(noIn) {
+ var node = new Node(Node.EQEXPR),
+ reltexpr = this.reltexpr(noIn);
+ if(this.look && {
+ '==': true,
+ '===': true,
+ '!==': true,
+ '!=': true
+ }.hasOwnProperty(this.look.content())) {
+ node.add(reltexpr);
+ while(this.look && {
+ '==': true,
+ '===': true,
+ '!==': true,
+ '!=': true
+ }.hasOwnProperty(this.look.content())) {
+ node.add(
+ this.match(),
+ this.reltexpr(noIn)
+ );
+ }
+ }
+ else {
+ return reltexpr;
+ }
+ return node;
+ },
+ reltexpr: function(noIn) {
+ var node = new Node(Node.RELTEXPR),
+ shiftexpr = this.shiftexpr();
+ if(this.look && ({
+ '<': true,
+ '>': true,
+ '>=': true,
+ '<=': true,
+ 'instanceof': true
+ }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
+ node.add(shiftexpr);
+ while(this.look && ({
+ '<': true,
+ '>': true,
+ '>=': true,
+ '<=': true,
+ 'instanceof': true
+ }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
+ node.add(
+ this.match(),
+ this.shiftexpr()
+ );
+ }
+ }
+ else {
+ return shiftexpr;
+ }
+ return node;
+ },
+ shiftexpr: function() {
+ var node = new Node(Node.SHIFTEXPR),
+ addexpr = this.addexpr();
+ if(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
+ node.add(addexpr);
+ while(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
+ node.add(
+ this.match(),
+ this.addexpr()
+ );
+ }
+ }
+ else {
+ return addexpr;
+ }
+ return node;
+ },
+ addexpr: function() {
+ var node = new Node(Node.ADDEXPR),
+ mtplexpr = this.mtplexpr();
+ if(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
+ node.add(mtplexpr);
+ while(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
+ node.add(
+ this.match(),
+ this.mtplexpr()
+ );
+ }
+ }
+ else {
+ return mtplexpr;
+ }
+ return node;
+ },
+ mtplexpr: function() {
+ var node = new Node(Node.MTPLEXPR),
+ unaryexpr = this.unaryexpr();
+ if(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
+ node.add(unaryexpr);
+ while(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
+ node.add(
+ this.match(),
+ this.unaryexpr()
+ );
+ }
+ }
+ else {
+ return unaryexpr;
+ }
+ return node;
+ },
+ unaryexpr: function() {
+ var node = new Node(Node.UNARYEXPR);
+ if(!this.look) {
+ this.error();
+ }
+ switch(this.look.content()) {
+ case 'delete':
+ case 'void':
+ case 'typeof':
+ case '++':
+ case '--':
+ case '+':
+ case '-':
+ case '~':
+ case '!':
+ node.add(
+ this.match(),
+ this.unaryexpr()
+ );
+ break;
+ default:
+ return this.postfixexpr();
+ }
+ return node;
+ },
+ postfixexpr: function() {
+ var node = new Node(Node.POSTFIXEXPR);
+ var leftexpr = this.leftexpr();
+ if(this.look && ['++', '--'].indexOf(this.look.content()) > -1 && !this.hasMoveLine) {
+ node.add(leftexpr);
+ while(this.look && ['++', '--'].indexOf(this.look.content()) > -1) {
+ node.add(this.match(undefined, true));
+ }
+ }
+ else {
+ return leftexpr;
+ }
+ return node;
+ },
+ leftexpr: function() {
+ if(this.look.content() == 'new') {
+ return this.newexpr();
+ }
+ else {
+ return this.callexpr();
+ }
+ },
+ newexpr: function(depth) {
+ depth = depth || 0;
+ var node = new Node(Node.NEWEXPR);
+ node.add(this.match('new'));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == 'new') {
+ node.add(this.newexpr(depth + 1));
+ }
+ else {
+ node.add(this.mmbexpr());
+ }
+ if(this.look && this.look.content() == '(') {
+ node.add(this.args());
+ }
+ if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
+ var mmb = new Node(Node.MMBEXPR);
+ mmb.add(node);
+ while(this.look) {
+ if(this.look.content() == '.') {
+ mmb.add(
+ this.match(),
+ this.match(Token.ID)
+ );
+ }
+ else if(this.look.content() == '[') {
+ mmb.add(
+ this.match(),
+ this.expr(),
+ this.match(']')
+ );
+ }
+ else {
+ break;
+ }
+ }
+ if(depth == 0 && this.look && this.look.content() == '(') {
+ var callexpr = this.callexpr(mmb);
+ return callexpr;
+ }
+ return mmb;
+ }
+ return node;
+ },
+ callexpr: function(mmb) {
+ var node = new Node(Node.CALLEXPR);
+ mmb = mmb || this.mmbexpr();
+ if(this.look && this.look.content() == '(') {
+ node.add(
+ mmb,
+ this.args()
+ );
+ if(this.look && ['.', '[', '('].indexOf(this.look.content()) > -1) {
+ while(this.look) {
+ if(this.look.content() == '.') {
+ node.add(
+ this.match(),
+ this.match(Token.ID)
+ );
+ }
+ else if(this.look.content() == '[') {
+ node.add(
+ this.match(),
+ this.expr(),
+ this.match(']')
+ );
+ }
+ else if(this.look.content() == '(') {
+ node.add(this.args());
+ }
+ else {
+ break;
+ }
+ }
+ }
+ }
+ else {
+ return mmb;
+ }
+ return node;
+ },
+ mmbexpr: function() {
+ var node = new Node(Node.MMBEXPR);
+ var mmb;
+ if(this.look.content() == 'function') {
+ mmb = this.fnexpr();
+ }
+ else {
+ mmb = this.prmrexpr();
+ }
+ if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
+ node.add(mmb);
+ while(this.look) {
+ if(this.look.content() == '.') {
+ node.add(
+ this.match(),
+ this.match(Token.ID)
+ );
+ }
+ else if(this.look.content() == '[') {
+ node.add(
+ this.match(),
+ this.expr(),
+ this.match(']')
+ );
+ }
+ else {
+ break;
+ }
+ }
+ }
+ else {
+ return mmb;
+ }
+ return node;
+ },
+ prmrexpr: function() {
+ var node = new Node(Node.PRMREXPR);
+ switch(this.look.type()) {
+ case Token.ID:
+ case Token.NUMBER:
+ case Token.STRING:
+ case Token.REG:
+ case Token.TEMPLATE:
+ node.add(this.match());
+ break;
+ default:
+ switch(this.look.content()) {
+ case 'this':
+ case 'null':
+ case 'true':
+ case 'false':
+ node.add(this.match());
+ break;
+ case '(':
+ node.add(this.match(), this.expr(), this.match(')'));
+ break;
+ case '[':
+ node.add(this.arrinit());
+ break;
+ case '{':
+ node.add(this.objltr());
+ break;
+ default:
+ this.error();
+ }
+ }
+ return node;
+ },
+ bindid: function() {
+ var node = new Node(Node.BINDID);
+ node.add(this.match('...'), this.assignexpr());
+ return node;
+ },
+ arrinit: function() {
+ //根据LL2分辨是arrltr还是arrcmph
+ //[assignexpr or [for
+ for(var i = this.index; i < this.length; i++) {
+ var next = this.tokens[i];
+ if(!S[next.tag()]) {
+ if(next.content() == 'for') {
+ return this.arrcmph();
+ }
+ else {
+ return this.arrltr();
+ }
+ }
+ }
+ this.error();
+ },
+ arrcmph: function() {
+ var node = new Node(Node.ARRCMPH);
+ node.add(this.match('['));
+ node.add(this.cmphfor());
+ node.add(this.match(']', 'missing ] after element list'));
+ return node;
+ },
+ cmphfor: function() {
+ var node = new Node(Node.CMPHFOR);
+ node.add(this.match('for'));
+ return node;
+ },
+ arrltr: function() {
+ var node = new Node(Node.ARRLTR);
+ node.add(this.match('['));
+ while(this.look && this.look.content() != ']' && this.look.content() != '...') {
+ if(this.look.content() == ',') {
+ node.add(this.match());
+ }
+ else {
+ node.add(this.assignexpr());
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
+ }
+ }
+ }
+ if(this.look.content() == '...') {
+ node.add(this.spread());
+ }
+ node.add(this.match(']', 'missing ] after element list'));
+ return node;
+ },
+ spread: function() {
+ var node = new Node(Node.SPREAD);
+ node.add(this.match('...'), this.assignexpr());
+ return node;
+ },
+ objltr: function() {
+ var node = new Node(Node.OBJLTR);
+ node.add(this.match('{'));
+ while(this.look && this.look.content() != '}') {
+ node.add(this.proptassign());
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
+ }
+ }
+ node.add(this.match('}', 'missing } after property list'));
+ return node;
+ },
+ proptassign: function() {
+ var node = new Node(Node.PROPTASSIGN);
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == 'get') {
+ node.add(this.match());
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == ':') {
+ node.add(this.match(), this.assignexpr());
+ }
+ else {
+ node.add(this.getfn());
+ }
+ }
+ else if(this.look.content() == 'set') {
+ node.add(this.match());
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() == ':') {
+ node.add(this.match(), this.assignexpr());
+ }
+ else {
+ node.add(this.setfn());
+ }
+ }
+ else {
+ switch(this.look.type()) {
+ case Token.ID:
+ case Token.STRING:
+ case Token.NUMBER:
+ node.add(
+ this.match(),
+ this.match(':', 'missing : after property id'),
+ this.assignexpr()
+ );
+ break;
+ default:
+ this.error('invalid property id');
+ }
+ }
+ return node;
+ },
+ getfn: function() {
+ var node = new Node(Node.GETFN);
+ node.add(
+ this.proptname(),
+ this.match('('),
+ this.match(')'),
+ this.match('{'),
+ this.fnbody(),
+ this.match('}')
+ );
+ return node;
+ },
+ setfn: function() {
+ var node = new Node(Node.SETFN);
+ node.add(
+ this.proptname(),
+ this.match('('),
+ this.propsets(),
+ this.match(')'),
+ this.match('{'),
+ this.fnbody(),
+ this.match('}')
+ );
+ return node;
+ },
+ proptname: function() {
+ var node = new Node(Node.PROPTNAME);
+ if(this.look) {
+ switch(this.look.type()) {
+ case Token.ID:
+ case Token.NUMBER:
+ case Token.STRING:
+ node.add(this.match());
+ break;
+ default:
+ this.error('missing name after . operator');
+ }
+ }
+ return node;
+ },
+ propsets: function() {
+ var node = new Node(Node.PROPTSETS);
+ node.add(this.match(Token.ID, 'setter functions must have one argument'));
+ return node;
+ },
+ args: function() {
+ var node = new Node(Node.ARGS);
+ node.add(this.match('('));
+ if(!this.look) {
+ this.error();
+ }
+ if(this.look.content() != ')') {
+ node.add(this.arglist());
+ }
+ node.add(this.match(')'));
+ return node;
+ },
+ arglist: function() {
+ var node = new Node(Node.ARGLIST);
+ while(this.look && this.look.content() != ')' && this.look.content() != '...') {
+ node.add(this.assignexpr());
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
+ }
+ }
+ if(this.look && this.look.content() == '...') {
+ node.add(this.bindid());
+ }
+ return node;
+ },
+ virtual: function(s) {
+ return new Node(Node.TOKEN, new Token(Token.VIRTUAL, s));
+ },
+ match: function(type, line, msg) {
+ if(typeof type == 'boolean') {
+ msg = line;
+ line = type;
+ type = undefined;
+ }
+ if(typeof line != 'boolean') {
+ line = false;
+ msg = line;
+ }
+ //未定义为所有非空白token
+ if(character.isUndefined(type)) {
+ if(this.look) {
+ var l = this.look;
+ this.move(line);
+ return new Node(Node.TOKEN, l);
+ }
+ else {
+ this.error('syntax error' + (msg || ''));
+ }
+ }
+ //或者根据token的type或者content匹配
+ else if(typeof type == 'string') {
+ //特殊处理;,不匹配但有换行或者末尾时自动补全,还有受限行
+ if(type == ';'
+ && (!this.look
+ || (this.look.content() != type && this.hasMoveLine)
+ || this.look.content() == '}')
+ ) {
+ if(this.look && S[this.look.type()]) {
+ this.move();
+ }
+ return this.virtual(';');
+ }
+ else if(this.look && this.look.content() == type) {
+ var l = this.look;
+ this.move(line);
+ return new Node(Node.TOKEN, l);
+ }
+ else {
+ this.error('missing ' + type + (msg || ''));
+ }
+ }
+ else if(typeof type == 'number') {
+ if(this.look && this.look.type() == type) {
+ var l = this.look;
+ this.move(line);
+ return new Node(Node.TOKEN, l);
+ }
+ else {
+ this.error('missing ' + Token.type(type) + (msg || ''));
+ }
+ }
+ },
+ move: function(line) {
+ this.lastLine = this.line;
+ this.lastCol = this.col;
+ //遗留下来的换行符
+ this.hasMoveLine = false;
+ do {
+ this.look = this.tokens[this.index++];
+ if(!this.look) {
+ return;
+ }
+ //存下忽略的token
+ if(S[this.look.type()]) {
+ this.ignores[this.index - 1] = this.look;
+ }
+ //包括line的情况下要跳出
+ if(this.look.type() == Token.LINE) {
+ this.line++;
+ this.col = 1;
+ this.hasMoveLine = true;
+ if(line) {
+ break;
+ }
+ }
+ else if(this.look.type() == Token.COMMENT) {
+ var s = this.look.content();
+ var n = character.count(this.look.content(), character.LINE);
+ if(n > 0) {
+ this.line += n;
+ var i = s.lastIndexOf(character.LINE);
+ this.col += s.length - i - 1;
+ this.hasMoveLine = true;
+ if(line) {
+ break;
+ }
+ }
+ }
+ else {
+ this.col += this.look.content().length;
+ if(!S[this.look.type()]) {
+ break;
+ }
+ }
+ } while(this.index <= this.length);
+ },
+ error: function(msg) {
+ msg = 'SyntaxError: ' + (msg || ' syntax error');
+ throw new Error(msg + ' line ' + this.lastLine + ' col ' + this.lastCol);
+ },
+ ignore: function() {
+ return this.ignores;
+ }
+});
+module.exports = Parser;
\ No newline at end of file
diff --git a/src/parser/js/Context.js b/src/parser/js/Context.js
index e58e8a7..e6ae1f4 100644
--- a/src/parser/js/Context.js
+++ b/src/parser/js/Context.js
@@ -316,15 +316,12 @@ function fnexpr(node, context) {
}
return child;
}
-//支持es6
+
function addParam(params, child) {
params.leaves().forEach(function(leaf, i) {
if(leaf.name() == JsNode.TOKEN && leaf.token().content() != ',') {
child.addParam(leaf.token().content());
}
- else if(leaf.name() == JsNode.RESTPARAM) {
- child.addParam(leaf.leaves()[1].token().content());
- }
});
}
function addAParam(params, child) {
diff --git a/src/parser/js/Parser.js b/src/parser/js/Parser.js
index 148c086..7ff2df5 100644
--- a/src/parser/js/Parser.js
+++ b/src/parser/js/Parser.js
@@ -55,9 +55,6 @@ var Parser = Class(function(lexer) {
if(this.look.content() == 'function') {
return this.fndecl();
}
- else if(this.look.content() == 'class') {
- return this.classdecl();
- }
else {
return this.stmt(allowSuper);
}
@@ -67,10 +64,6 @@ var Parser = Class(function(lexer) {
this.error();
}
switch(this.look.content()) {
- case 'let':
- return this.letstmt();
- case 'const':
- return this.cststmt();
case 'var':
return this.varstmt();
case '{':
@@ -99,13 +92,6 @@ var Parser = Class(function(lexer) {
return this.trystmt();
case 'debugger':
return this.debstmt();
- case 'super':
- if(!allowSuper) {
- this.error('super must in a class');
- }
- return this.superstmt();
- case 'import':
- return this.imptstmt();
default:
if(this.look.type() == Token.ID) {
for(var i = this.index; i < this.length; i++) {
@@ -128,40 +114,6 @@ var Parser = Class(function(lexer) {
node.add(this.expr(), this.match(';'));
return node;
},
- cststmt: function(noSem) {
- var node = new Node(Node.CSTSTMT);
- node.add(
- this.match('const'),
- this.vardecl()
- );
- while(this.look && this.look.content() == ',') {
- node.add(
- this.match(),
- this.vardecl()
- );
- }
- if(!noSem) {
- node.add(this.match(';'));
- }
- return node;
- },
- letstmt: function(noSem) {
- var node = new Node(Node.LETSTMT);
- node.add(
- this.match('let'),
- this.vardecl()
- );
- while(this.look && this.look.content() == ',') {
- node.add(
- this.match(),
- this.vardecl()
- );
- }
- if(!noSem) {
- node.add(this.match(';'));
- }
- return node;
- },
varstmt: function(noSem) {
var node = new Node(Node.VARSTMT);
node.add(
@@ -184,109 +136,12 @@ var Parser = Class(function(lexer) {
if(!this.look) {
this.error('missing variable name');
}
- if(['[', '{'].indexOf(this.look.content()) > -1) {
- node.add(this.bindpat());
- if(!this.look || this.look.content() != '=') {
- this.error('missing = in destructuring declaration');
- }
- node.add(this.assign());
- }
- else {
- node.add(this.match(Token.ID, 'missing variable name'));
- if(this.look && this.look.content() == '=') {
- node.add(this.assign());
- }
- }
- return node;
- },
- bindpat: function() {
- if(this.look.content() == '[') {
- return this.arrbindpat();
- }
- else if(this.look.content() == '{') {
- return this.objbindpat();
- }
- },
- arrbindpat: function() {
- var node = new Node(Node.ARRBINDPAT);
- node.add(this.match('['));
- while(this.look && this.look.content() != ']') {
- if(this.look.content() == ',') {
- node.add(this.match());
- }
- else if(this.look.content() == '...') {
- break;
- }
- else {
- node.add(this.bindelem());
- }
- }
- if(this.look.content() == '...') {
- node.add(this.restparam());
- }
- node.add(this.match(']', 'missing ] after element list'));
- return node;
- },
- bindelem: function() {
- var node = new Node(Node.BINDELEM);
- if(['[', '{'].indexOf(this.look.content()) > -1) {
- node.add(this.bindpat());
- if(this.look && this.look.content() == '=') {
- node.add(this.assign());
- }
- }
- else {
- return this.singlename();
- }
- return node;
- },
- singlename: function() {
- var node = new Node(Node.SINGLENAME);
- node.add(this.match(Token.ID));
+ node.add(this.match(Token.ID, 'missing variable name'));
if(this.look && this.look.content() == '=') {
node.add(this.assign());
}
return node;
},
- objbindpat: function() {
- var node = new Node(Node.OBJBINDPAT);
- node.add(this.match('{'));
- while(this.look && this.look.content() != '}') {
- node.add(this.bindpropt());
- if(this.look && this.look.content() == ',') {
- node.add(this.match());
- }
- }
- node.add(this.match('}', 'missing } after property list'));
- return node;
- },
- bindpropt: function() {
- var node = new Node(Node.BINDPROPT);
- switch(this.look.type()) {
- case Token.ID:
- case Token.STRING:
- case Token.NUMBER:
- break;
- default:
- this.error('invalid property id');
- }
- //根据LL2分辨是PropertyName[?Yield, ?GeneratorParameter] : BindingElement[?Yield, ?GeneratorParameter]
- //还是SingleNameBinding [?Yield, ?GeneratorParameter]
- for(var i = this.index; i < this.length; i++) {
- var next = this.tokens[i];
- if(!S[next.tag()]) {
- if(next.content() == ':') {
- node.add(this.match(), this.match());
- node.add(this.bindelem());
- }
- else {
- node.add(this.singlename());
- }
- return node;
- }
- }
- this.error('missing : after property id');
- },
assign: function() {
var node = new Node(Node.ASSIGN);
node.add(this.match('='));
@@ -600,42 +455,6 @@ var Parser = Class(function(lexer) {
);
return node;
},
- superstmt: function() {
- var node = new Node(Node.SUPERSTMT);
- node.add(this.match('super'));
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == '.') {
- while(this.look && this.look.content() == '.') {
- node.add(this.match());
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == 'super') {
- node.add(this.match());
- }
- else {
- break;
- }
- }
- if(this.look.content() != '(') {
- node.add(this.match(Token.ID));
- while(this.look && this.look.content() == '.') {
- node.add(this.match(), this.match(Token.ID));
- }
- }
- }
- node.add(
- this.args(),
- this.match(';')
- );
- return node;
- },
- imptstmt: function() {
- var node = new Node(Node.IMPTSTMT);
- return node;
- },
fndecl: function() {
var node = new Node(Node.FNDECL);
node.add(
@@ -683,36 +502,12 @@ var Parser = Class(function(lexer) {
},
fnparams: function() {
var node = new Node(Node.FNPARAMS);
- while(this.look && this.look.content() != ')' && this.look.content() != '...') {
+ while(this.look && this.look.content() != ')') {
node.add(this.match(Token.ID, 'missing formal parameter'));
- if(this.look) {
- if(this.look.content() == ',') {
- node.add(this.match());
- }
- else if(this.look.content() == '=') {
- node.add(this.bindelement());
- if(this.look && this.look.content() == ',') {
- node.add(this.match());
- }
- }
+ if(this.look && this.look.content() == ',') {
+ node.add(this.match());
}
}
- if(!this.look) {
- this.error('missing ) after formal parameters');
- }
- if(this.look.content() == '...') {
- node.add(this.restparam());
- }
- return node;
- },
- bindelement: function() {
- var node = new Node(Node.BINDELEMENT);
- node.add(this.match('='), this.assignexpr());
- return node;
- },
- restparam: function() {
- var node = new Node(Node.RESTPARAM);
- node.add(this.match('...'), this.match(Token.ID));
return node;
},
fnbody: function(allowSuper) {
@@ -722,76 +517,6 @@ var Parser = Class(function(lexer) {
}
return node;
},
- classdecl: function() {
- var node = new Node(Node.CLASSDECL);
- node.add(this.match('class'), this.match(Token.ID));
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == 'extends') {
- node.add(this.heratige());
- }
- node.add(
- this.match('{'),
- this.classbody(),
- this.match('}')
- );
- return node;
- },
- heratige: function() {
- var node = new Node(Node.HERITAGE);
- node.add(this.match('extends'), this.match(Token.ID));
- return node;
- },
- classbody: function() {
- var node = new Node(Node.CLASSBODY),
- methods = {},
- hasStatic = false;
- while(this.look && this.look.content() != '}') {
- if(this.look.content() == ';') {
- node.add(this.match());
- continue;
- }
- hasStatic = false;
- if(this.look.content() == 'static') {
- node.add(this.match());
- hasStatic = true;
- }
- if(!this.look) {
- this.error();
- }
- node.add(this.method(hasStatic, methods));
- }
- return node;
- },
- method: function(hasStatic, methods, statics) {
- var node = new Node(Node.METHOD);
- if(this.look.content() == 'get') {
- node.add(this.match(), this.getfn());
- }
- else if(this.look.content() == 'set') {
- node.add(this.match(), this.setfn());
- }
- else {
- node.add(this.match(Token.ID));
- var id = node.leaves()[0].token().content();
- if(methods.hasOwnProperty(id)) {
- this.error('duplicate method decl in class');
- }
- methods[id] = true;
- node.add(this.match('('));
- if(this.look.content() != ')') {
- node.add(this.fnparams());
- }
- node.add(
- this.match(')'),
- this.match('{'),
- this.fnbody(true),
- this.match('}', 'missing } in compound statement')
- );
- }
- return node;
- },
expr: function(noIn) {
var node = new Node(Node.EXPR),
assignexpr = this.assignexpr(noIn);
@@ -1225,7 +950,7 @@ var Parser = Class(function(lexer) {
node.add(this.match(), this.expr(), this.match(')'));
break;
case '[':
- node.add(this.arrinit());
+ node.add(this.arrltr());
break;
case '{':
node.add(this.objltr());
@@ -1236,64 +961,20 @@ var Parser = Class(function(lexer) {
}
return node;
},
- bindid: function() {
- var node = new Node(Node.BINDID);
- node.add(this.match('...'), this.assignexpr());
- return node;
- },
- arrinit: function() {
- //根据LL2分辨是arrltr还是arrcmph
- //[assignexpr or [for
- for(var i = this.index; i < this.length; i++) {
- var next = this.tokens[i];
- if(!S[next.tag()]) {
- if(next.content() == 'for') {
- return this.arrcmph();
- }
- else {
- return this.arrltr();
- }
- }
- }
- this.error();
- },
- arrcmph: function() {
- var node = new Node(Node.ARRCMPH);
- node.add(this.match('['));
- node.add(this.cmphfor());
- node.add(this.match(']', 'missing ] after element list'));
- return node;
- },
- cmphfor: function() {
- var node = new Node(Node.CMPHFOR);
- node.add(this.match('for'));
- return node;
- },
arrltr: function() {
var node = new Node(Node.ARRLTR);
node.add(this.match('['));
- while(this.look && this.look.content() != ']' && this.look.content() != '...') {
+ while(this.look && this.look.content() != ']') {
if(this.look.content() == ',') {
node.add(this.match());
}
else {
node.add(this.assignexpr());
- if(this.look && this.look.content() == ',') {
- node.add(this.match());
- }
}
}
- if(this.look.content() == '...') {
- node.add(this.spread());
- }
node.add(this.match(']', 'missing ] after element list'));
return node;
},
- spread: function() {
- var node = new Node(Node.SPREAD);
- node.add(this.match('...'), this.assignexpr());
- return node;
- },
objltr: function() {
var node = new Node(Node.OBJLTR);
node.add(this.match('{'));
@@ -1308,95 +989,21 @@ var Parser = Class(function(lexer) {
},
proptassign: function() {
var node = new Node(Node.PROPTASSIGN);
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == 'get') {
- node.add(this.match());
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == ':') {
- node.add(this.match(), this.assignexpr());
- }
- else {
- node.add(this.getfn());
- }
- }
- else if(this.look.content() == 'set') {
- node.add(this.match());
- if(!this.look) {
- this.error();
- }
- if(this.look.content() == ':') {
- node.add(this.match(), this.assignexpr());
- }
- else {
- node.add(this.setfn());
- }
- }
- else {
- switch(this.look.type()) {
- case Token.ID:
- case Token.STRING:
- case Token.NUMBER:
- node.add(
- this.match(),
- this.match(':', 'missing : after property id'),
- this.assignexpr()
- );
- break;
- default:
- this.error('invalid property id');
- }
- }
- return node;
- },
- getfn: function() {
- var node = new Node(Node.GETFN);
- node.add(
- this.proptname(),
- this.match('('),
- this.match(')'),
- this.match('{'),
- this.fnbody(),
- this.match('}')
- );
- return node;
- },
- setfn: function() {
- var node = new Node(Node.SETFN);
- node.add(
- this.proptname(),
- this.match('('),
- this.propsets(),
- this.match(')'),
- this.match('{'),
- this.fnbody(),
- this.match('}')
- );
- return node;
- },
- proptname: function() {
- var node = new Node(Node.PROPTNAME);
- if(this.look) {
- switch(this.look.type()) {
- case Token.ID:
- case Token.NUMBER:
- case Token.STRING:
- node.add(this.match());
- break;
- default:
- this.error('missing name after . operator');
- }
+ switch(this.look.type()) {
+ case Token.ID:
+ case Token.STRING:
+ case Token.NUMBER:
+ node.add(
+ this.match(),
+ this.match(':', 'missing : after property id'),
+ this.assignexpr()
+ );
+ break;
+ default:
+ this.error('invalid property id');
}
return node;
},
- propsets: function() {
- var node = new Node(Node.PROPTSETS);
- node.add(this.match(Token.ID, 'setter functions must have one argument'));
- return node;
- },
args: function() {
var node = new Node(Node.ARGS);
node.add(this.match('('));
@@ -1411,15 +1018,12 @@ var Parser = Class(function(lexer) {
},
arglist: function() {
var node = new Node(Node.ARGLIST);
- while(this.look && this.look.content() != ')' && this.look.content() != '...') {
+ while(this.look && this.look.content() != ')') {
node.add(this.assignexpr());
if(this.look && this.look.content() == ',') {
node.add(this.match());
}
}
- if(this.look && this.look.content() == '...') {
- node.add(this.bindid());
- }
return node;
},
virtual: function(s) {
diff --git a/tests/covrage.html b/tests/covrage.html
index 085a714..e8986f3 100644
--- a/tests/covrage.html
+++ b/tests/covrage.html
@@ -351,4 +351,4 @@
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }
-
Coverage
/Users/army/Sites/homunculus/src/lexer/Lexer.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../util/Class'); |
2 | 1 | var character = require('../util/character'); |
3 | 1 | var Token = require('./Token'); |
4 | 1 | var Lexer = Class(function(rule) { |
5 | 274 | this.rule = rule; //当前语法规则 |
6 | 274 | this.init(); |
7 | | }).methods({ |
8 | | init: function() { |
9 | 275 | this.code = ''; //要解析的代码 |
10 | 275 | this.peek = ''; //向前看字符 |
11 | 275 | this.index = 0; //向前看字符字符索引 |
12 | 275 | this.isReg = Lexer.IS_REG; //当前/是否是perl风格正则表达式 |
13 | 275 | this.tokenList = []; //结果的token列表 |
14 | 275 | this.parentheseState = false; //(开始时标记之前终结符是否为if/for/while等关键字 |
15 | 275 | this.parentheseStack = []; //圆括号深度记录当前是否为if/for/while等语句内部 |
16 | 275 | this.cacheLine = 0; //行缓存值 |
17 | 275 | this.totalLine = 1; //总行数 |
18 | 275 | this.colNum = 0; //列 |
19 | 275 | this.colMax = 0; //最大列数 |
20 | | }, |
21 | | parse: function(code) { |
22 | 231 | this.code = code || ''; |
23 | 231 | var temp = []; |
24 | 231 | this.scan(temp); |
25 | 222 | return temp; |
26 | | }, |
27 | | parseOn: function() { |
28 | 1 | var temp = []; |
29 | 1 | this.scan(temp); |
30 | 1 | return temp; |
31 | | }, |
32 | | tokens: function() { |
33 | 196 | return this.tokenList; |
34 | | }, |
35 | | scan: function(temp) { |
36 | 232 | var perlReg = this.rule.perlReg(); |
37 | 232 | var length = this.code.length; |
38 | 232 | var count = 0; |
39 | | outer: |
40 | | while(this.index < length) { |
41 | 454813 | if(this.cacheLine > 0 && count >= this.cacheLine) { |
42 | 1 | break; |
43 | | } |
44 | 454812 | this.readch(); |
45 | | //perl风格正则 |
46 | 454812 | if(perlReg && this.isReg == Lexer.IS_REG && this.peek == character.SLASH && !{ '/': true, '*': true }[this.code.charAt(this.index)]) { |
47 | 493 | this.dealReg(temp, length); |
48 | 489 | this.isReg = Lexer.NOT_REG; |
49 | | } |
50 | | //依次遍历匹配规则,命中则继续 |
51 | | else { |
52 | 454319 | for(var i = 0, matches = this.rule.matches(), len = matches.length; i < len; i++) { |
53 | 7888002 | var match = matches[i]; |
54 | 7888002 | if(match.match(this.peek, this.code, this.index)) { |
55 | 454318 | var token = new Token(match.tokenType(), match.content(), match.val(), this.index - 1); |
56 | 454318 | var error = match.error(); |
57 | 454318 | var matchLen = match.content().length; |
58 | 454318 | if(token.type() == Token.ID && this.rule.keyWords().hasOwnProperty(token.content())) { |
59 | 21502 | token.type(Token.KEYWORD); |
60 | | } |
61 | 454318 | temp.push(token); |
62 | 454318 | this.tokenList.push(token); |
63 | 454318 | this.index += matchLen - 1; |
64 | 454318 | var n = character.count(token.val(), character.LINE); |
65 | 454318 | count += n; |
66 | 454318 | this.totalLine += n; |
67 | 454318 | if(n) { |
68 | 32320 | var j = match.content().indexOf(character.LINE); |
69 | 32320 | var k = match.content().lastIndexOf(character.LINE); |
70 | 32320 | this.colMax = Math.max(this.colMax, this.colNum + j); |
71 | 32320 | this.colNum = match.content().length - k; |
72 | | } |
73 | | else { |
74 | 421998 | this.colNum += matchLen; |
75 | | } |
76 | 454318 | this.colMax = Math.max(this.colMax, this.colNum); |
77 | 454318 | if(error) { |
78 | 5 | this.error(error, this.code.slice(this.index - matchLen, this.index)); |
79 | | } |
80 | | //支持perl正则需判断关键字、圆括号对除号语义的影响 |
81 | 454314 | if(perlReg && match.perlReg() != Lexer.IGNORE) { |
82 | 258495 | if(match.perlReg() == Lexer.SPECIAL) { |
83 | 96102 | this.isReg = match.special(); |
84 | | } |
85 | | else { |
86 | 162393 | this.isReg = match.perlReg(); |
87 | | } |
88 | 258495 | if(this.peek == character.LEFT_PARENTHESE) { |
89 | 19892 | this.parentheseStack.push(this.parentheseState); |
90 | 19892 | this.parentheseState = false; |
91 | | } |
92 | 238603 | else if(this.peek == character.RIGHT_PARENTHESE) { |
93 | 19875 | this.isReg = this.parentheseStack.pop() ? Lexer.IS_REG : Lexer.NOT_REG; |
94 | | } |
95 | | else { |
96 | 218728 | this.parentheseState = match.parenthese(); |
97 | | } |
98 | | } |
99 | 454314 | continue outer; |
100 | | } |
101 | | } |
102 | | //如果有未匹配的,说明规则不完整,抛出错误 |
103 | 1 | this.error('unknow token'); |
104 | | } |
105 | | } |
106 | 223 | return this; |
107 | | }, |
108 | | readch: function() { |
109 | 463674 | this.peek = this.code.charAt(this.index++); |
110 | | }, |
111 | | dealReg: function(temp, length) { |
112 | 493 | var lastIndex = this.index - 1; |
113 | 493 | var res = false; |
114 | | outer: |
115 | | do { |
116 | 6803 | this.readch(); |
117 | 6803 | if(this.peek == character.LINE) { |
118 | 2 | this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index)); |
119 | 1 | break; |
120 | | } |
121 | 6801 | else if(this.peek == character.BACK_SLASH) { |
122 | 490 | this.index++; |
123 | | } |
124 | 6311 | else if(this.peek == character.LEFT_BRACKET) { |
125 | 264 | do { |
126 | 1368 | this.readch(); |
127 | 1368 | if(this.peek == character.LINE) { |
128 | 2 | this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index)); |
129 | 1 | break outer; |
130 | | } |
131 | 1366 | else if(this.peek == character.BACK_SLASH) { |
132 | 265 | this.index++; |
133 | | } |
134 | 1101 | else if(this.peek == character.RIGHT_BRACKET) { |
135 | 262 | continue outer; |
136 | | } |
137 | | } while(this.index < length); |
138 | | } |
139 | 6047 | else if(this.peek == character.SLASH) { |
140 | 488 | res = true; |
141 | 488 | var hash = {}; |
142 | 488 | var flag = { |
143 | | 'g': true, |
144 | | 'i': true, |
145 | | 'm': true, |
146 | | 'y': true |
147 | | }; |
148 | | //正则的flag中有gimy4种,大小写敏感且不能重复 |
149 | 488 | do { |
150 | 691 | this.readch(); |
151 | 691 | if(character.isLetter(this.peek)) { |
152 | 205 | if(hash.hasOwnProperty(this.peek) || !flag.hasOwnProperty(this.peek)) { |
153 | 2 | this.error('SyntaxError: invalid regular expression flag ' + this.peek, this.code.slice(lastIndex, this.index)); |
154 | 1 | break outer; |
155 | | } |
156 | 203 | hash[this.peek] = true; |
157 | | } |
158 | | else { |
159 | 486 | break outer; |
160 | | } |
161 | | } while(this.index <= length); |
162 | | } |
163 | | } while(this.index < length); |
164 | 490 | if(!res) { |
165 | 3 | this.error('SyntaxError: unterminated regular expression literal', this.code.slice(lastIndex, this.index - 1)); |
166 | | } |
167 | 489 | var token = new Token(Token.REG, this.code.slice(lastIndex, --this.index), lastIndex); |
168 | 489 | temp.push(token); |
169 | 489 | this.tokenList.push(token); |
170 | 489 | this.colNum += this.index - lastIndex; |
171 | 489 | this.colMax = Math.max(this.colMax, this.colNum); |
172 | 489 | return this; |
173 | | }, |
174 | | cache: function(i) { |
175 | 1 | if(!character.isUndefined(i) && i !== null) { |
176 | 1 | this.cacheLine = i; |
177 | | } |
178 | 1 | return this.cacheLine; |
179 | | }, |
180 | | finish: function() { |
181 | 2 | return this.index >= this.code.length; |
182 | | }, |
183 | | line: function() { |
184 | 16 | return this.totalLine; |
185 | | }, |
186 | | col: function() { |
187 | 1 | return this.colMax; |
188 | | }, |
189 | | error: function(s, str) { |
190 | 15 | if(character.isUndefined(str)) { |
191 | 1 | str = this.code.substr(this.index - 1, 20); |
192 | | } |
193 | 15 | if(Lexer.mode() === Lexer.STRICT) { |
194 | 9 | throw new Error(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str); |
195 | | } |
196 | 6 | else if(Lexer.mode() === Lexer.LOOSE && !character.isUndefined(console)) { |
197 | 6 | if(console.warn) { |
198 | 6 | console.warn(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str); |
199 | | } |
200 | | } |
201 | 6 | return this; |
202 | | } |
203 | | }).statics({ |
204 | | IGNORE: 0, |
205 | | IS_REG: 1, |
206 | | NOT_REG: 2, |
207 | | SPECIAL: 3, |
208 | | STRICT: 0, |
209 | | LOOSE: 1, |
210 | | mode: function(i) { |
211 | 33 | if(!character.isUndefined(i)) { |
212 | 10 | cmode = i; |
213 | | } |
214 | 33 | return cmode; |
215 | | } |
216 | | }); |
217 | 1 | var cmode = Lexer.STRICT; |
218 | 1 | module.exports = Lexer; |
/Users/army/Sites/homunculus/src/lexer/Token.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../util/Class'); |
2 | 1 | var character = require('../util/character'); |
3 | 1 | var tid = 0; |
4 | 1 | var types; |
5 | 1 | var Token = Class(function(type, content, val, sIndex) { |
6 | 456402 | this.t = type; //token类型 |
7 | 456402 | this.c = content; //token的字面内容,string包括头尾的引号 |
8 | 456402 | if(character.isNumber(val)) { |
9 | 494 | sIndex = val; |
10 | 494 | val = content; |
11 | | } |
12 | 455908 | else if(character.isUndefined(val)) { |
13 | 1590 | val = content; |
14 | 1590 | sIndex = -1; |
15 | | } |
16 | 456402 | this.v = val; //token的值,一般情况下等于content,特殊如string情况下值是不加头尾的引号 |
17 | 456402 | this.id = tid++; //token的索引 |
18 | 456402 | this.si = sIndex; //token在源码字符串中的索引 |
19 | | }).methods({ |
20 | | type: function(t) { |
21 | 1637792 | if(!character.isUndefined(t)) { |
22 | 21502 | this.t = t; |
23 | | } |
24 | 1637792 | return this.t; |
25 | | }, |
26 | | content: function(c) { |
27 | 1704074 | if(!character.isUndefined(c)) { |
28 | 1 | this.c = c; |
29 | | } |
30 | 1704074 | return this.c; |
31 | | }, |
32 | | val: function(v) { |
33 | 454320 | if(!character.isUndefined(v)) { |
34 | 1 | this.v = v; |
35 | | } |
36 | 454320 | return this.v; |
37 | | }, |
38 | | tag: function(t) { |
39 | 8 | if(!character.isUndefined(t)) { |
40 | 1 | this.t = t; |
41 | | } |
42 | 8 | return Token.type(this.t); |
43 | | }, |
44 | | tid: function(id) { |
45 | 2 | if(!character.isUndefined(id)) { |
46 | 1 | this.id = id; |
47 | | } |
48 | 2 | return this.id; |
49 | | }, |
50 | | sIndex: function(si) { |
51 | 6 | if(!character.isUndefined(si)) { |
52 | 1 | this.si = si; |
53 | | } |
54 | 6 | return this.si; |
55 | | } |
56 | | }).statics({ |
57 | | IGNORE: -2, |
58 | | VIRTUAL: -1, |
59 | | OTHER: 0, |
60 | | BLANK: 1, |
61 | | TAB: 2, |
62 | | LINE: 3, |
63 | | NUMBER: 4, |
64 | | ID: 5, |
65 | | COMMENT: 6, |
66 | | STRING: 7, |
67 | | SIGN: 8, |
68 | | REG: 9, |
69 | | KEYWORD: 10, |
70 | | ANNOT: 11, |
71 | | HEAD: 12, |
72 | | TEMPLATE: 13, |
73 | | ENTER: 14, |
74 | | PROPERTY: 15, |
75 | | VARS: 16, |
76 | | HACK: 17, |
77 | | IMPORTANT: 18, |
78 | | type: function(tag) { |
79 | 13 | if(character.isUndefined(types)) { |
80 | 1 | types = []; |
81 | 1 | Object.keys(Token).forEach(function(o) { |
82 | 25 | if(typeof Token[o] == 'number') { |
83 | 21 | types[Token[o]] = o; |
84 | | } |
85 | | }); |
86 | | } |
87 | 13 | return types[tag]; |
88 | | } |
89 | | }); |
90 | 1 | module.exports = Token; |
/Users/army/Sites/homunculus/src/lexer/match/CharacterSet.js
Line | Hits | Source |
---|
1 | 1 | var Match = require('./Match'); |
2 | 1 | var character = require('../../util/character'); |
3 | 1 | var CharacterSet = Match.extend(function(type, str, setPReg) { |
4 | 544 | Match.call(this, type, setPReg); |
5 | 544 | this.str = str; |
6 | | }).methods({ |
7 | | match: function(c, code, index) { |
8 | 141104 | var isIn = this.str.indexOf(c) > -1; |
9 | 141104 | if(isIn) { |
10 | 133573 | this.result = c; |
11 | | } |
12 | 141104 | return isIn; |
13 | | } |
14 | | }); |
15 | 1 | module.exports = CharacterSet; |
/Users/army/Sites/homunculus/src/lexer/match/CompleteEqual.js
Line | Hits | Source |
---|
1 | 1 | var Match = require('./Match'); |
2 | 1 | var character = require('../../util/character'); |
3 | 1 | var CompleteEqual = Match.extend(function(type, result, setPReg) { |
4 | 10192 | Match.call(this, type, setPReg); |
5 | 10192 | this.result = result; |
6 | | }).methods({ |
7 | | match: function(c, code, index) { |
8 | 6038931 | return code.substr(--index, this.result.length) == this.result; |
9 | | } |
10 | | }); |
11 | 1 | module.exports = CompleteEqual; |
/Users/army/Sites/homunculus/src/lexer/match/LineParse.js
Line | Hits | Source |
---|
1 | 1 | var Match = require('./Match'); |
2 | 1 | var Token = require('../Token'); |
3 | 1 | var character = require('../../util/character'); |
4 | 1 | var LineParse = Match.extend(function(type, begin, end, mutiline, setPReg) { |
5 | 820 | if(character.isUndefined(mutiline)) { |
6 | 0 | mutiline = false; |
7 | | } |
8 | 820 | Match.call(this, type, setPReg); |
9 | 820 | this.begin = begin; |
10 | 820 | this.end = end; |
11 | 820 | this.msg = null; |
12 | 820 | this.mutiline = mutiline; |
13 | | }).methods({ |
14 | | match: function(c, code, index) { |
15 | 763466 | this.msg = null; |
16 | 763466 | if(this.begin == code.charAt(index - 1)) { |
17 | 6829 | var len = code.length, |
18 | | lastIndex = index - 1, |
19 | | res = false; |
20 | 6829 | while(index < len) { |
21 | 57362 | var c = code.charAt(index++); |
22 | | //转义 |
23 | 57362 | if(c == character.BACK_SLASH) { |
24 | 402 | if(code.charAt(index++) == character.ENTER) { |
25 | 1 | index++; |
26 | | } |
27 | | } |
28 | 56960 | else if(c == character.LINE && !this.mutiline) { |
29 | 1 | break; |
30 | | } |
31 | 56959 | else if(c == this.end) { |
32 | 6827 | res = true; |
33 | 6827 | break; |
34 | | } |
35 | | } |
36 | 6829 | if(!res) { |
37 | 2 | this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase() + ' literal'; |
38 | | } |
39 | 6829 | this.result = code.slice(lastIndex, index); |
40 | 6829 | return true; |
41 | | } |
42 | 756637 | return false; |
43 | | }, |
44 | | error: function() { |
45 | 6829 | return this.msg; |
46 | | }, |
47 | | val: function() { |
48 | 6829 | return this.content().slice(this.begin.length, -this.end.length); |
49 | | } |
50 | | }); |
51 | 1 | module.exports = LineParse; |
/Users/army/Sites/homunculus/src/lexer/match/LineSearch.js
Line | Hits | Source |
---|
1 | 1 | var Match = require('./Match'); |
2 | 1 | var Token = require('../Token'); |
3 | 1 | var character = require('../../util/character'); |
4 | 1 | var LineSearch = Match.extend(function(type, begin, end, contain, setPReg) { |
5 | 548 | if(character.isUndefined(contain)) { |
6 | 274 | contain = false; |
7 | | } |
8 | 548 | Match.call(this, type, setPReg); |
9 | 548 | this.begin = begin; |
10 | 548 | this.end = end; |
11 | 548 | this.contain = contain; |
12 | 548 | this.msg = null; |
13 | | }).methods({ |
14 | | match: function(c, code, index) { |
15 | 522214 | this.msg = null; |
16 | 522214 | if(this.begin == code.substr(--index, this.begin.length)) { |
17 | | //支持多个end匹配时不支持包含选项 |
18 | 5001 | if(!this.contain && Array.isArray(this.end)) { |
19 | 4786 | for(var j = 0, len = this.end.length; j < len; j++) { |
20 | 14358 | var i = code.indexOf(this.end[j], index + this.begin.length); |
21 | 14358 | if(i != -1) { |
22 | 4785 | this.result = code.slice(index, i); |
23 | 4785 | return true; |
24 | | } |
25 | | } |
26 | | //都不匹配时到末尾 |
27 | 1 | this.result = code.slice(index); |
28 | 1 | return true; |
29 | | } |
30 | | else { |
31 | 215 | var i = code.indexOf(this.end, index + this.begin.length); |
32 | 215 | if(i == -1) { |
33 | 1 | if(this.contain) { |
34 | 1 | this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase(); |
35 | | } |
36 | 1 | i = code.length; |
37 | | } |
38 | 214 | else if(this.contain) { |
39 | 214 | i += this.end.length; |
40 | | } |
41 | 215 | this.result = code.slice(index, i); |
42 | 215 | return true; |
43 | | } |
44 | | } |
45 | 517213 | return false; |
46 | | }, |
47 | | error: function() { |
48 | 5001 | return this.msg; |
49 | | } |
50 | | }); |
51 | 1 | module.exports = LineSearch; |
/Users/army/Sites/homunculus/src/lexer/match/Match.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../../util/Class'); |
2 | 1 | var character = require('../../util/character'); |
3 | 1 | var Lexer = require('../Lexer'); |
4 | 1 | module.exports = Class(function(type, setPReg, special, parenthese) { |
5 | 13220 | this.type = type; |
6 | 13220 | if(character.isUndefined(setPReg)) { |
7 | 2884 | setPReg = Lexer.IGNORE; |
8 | | } |
9 | 13220 | this.setPReg = setPReg; |
10 | 13220 | this.result = null; |
11 | | //忽略0,是1,否2,特殊3 |
12 | 13220 | if(setPReg) { |
13 | 10336 | if(character.isUndefined(special)) { |
14 | 10064 | special = function() { |
15 | 0 | return Lexer.IGNORE; |
16 | | }; |
17 | | } |
18 | 10336 | if(character.isUndefined(parenthese)) { |
19 | 10064 | parenthese = function() { |
20 | 122626 | return false; |
21 | | }; |
22 | | } |
23 | | } |
24 | 13220 | this.special = special; |
25 | 13220 | this.parenthese = parenthese; |
26 | | }).methods({ |
27 | | tokenType: function() { |
28 | 454318 | return this.type; |
29 | | }, |
30 | | perlReg: function() { |
31 | 875202 | return this.setPReg; |
32 | | }, |
33 | | val: function() { |
34 | 447489 | return this.content(); |
35 | | }, |
36 | | content: function() { |
37 | 1652118 | return this.result; |
38 | | }, |
39 | | match: function(c, code, index) { |
40 | | //需被实现 |
41 | 0 | throw new Error('match needs to be implement'); |
42 | | }, |
43 | | error: function() { |
44 | 338849 | return false; |
45 | | } |
46 | | }); |
/Users/army/Sites/homunculus/src/lexer/match/RegMatch.js
Line | Hits | Source |
---|
1 | 1 | var Match = require('./Match'); |
2 | 1 | var RegMatch = Match.extend(function(type, reg, valid, setPReg, special, parenthese) { |
3 | 1116 | if(typeof valid == 'number') { |
4 | 272 | parenthese = special; |
5 | 272 | special = setPReg; |
6 | 272 | setPReg = valid; |
7 | 272 | valid = null; |
8 | | } |
9 | 1116 | Match.call(this, type, setPReg, special, parenthese); |
10 | 1116 | this.reg = reg; |
11 | 1116 | this.valid = valid; |
12 | | }).methods({ |
13 | | match: function(c, code, index) { |
14 | 422287 | var self = this, |
15 | | res = self.reg.exec(code.slice(index - 1)); |
16 | 422287 | self.msg = null; |
17 | 422287 | if(res) { |
18 | 103639 | self.result = res[0]; |
19 | 103639 | if(self.valid) { |
20 | 7537 | for(var i = 0, keys = Object.keys(self.valid), len = keys.length; i < len; i++) { |
21 | 7537 | if(self.valid[keys[i]].test(self.result)) { |
22 | 2 | self.msg = keys[i]; |
23 | 2 | break; |
24 | | } |
25 | | } |
26 | | } |
27 | 103639 | return true; |
28 | | } |
29 | 318648 | return false; |
30 | | }, |
31 | | error: function() { |
32 | 103639 | return this.msg; |
33 | | } |
34 | | }); |
35 | 1 | module.exports = RegMatch; |
/Users/army/Sites/homunculus/src/lexer/rule/EcmascriptRule.js
Line | Hits | Source |
---|
1 | 1 | var Rule = require('./Rule'); |
2 | 1 | var LineSearch = require('../match/LineSearch'); |
3 | 1 | var LineParse = require('../match/LineParse'); |
4 | 1 | var CompleteEqual = require('../match/CompleteEqual'); |
5 | 1 | var RegMatch = require('../match/RegMatch'); |
6 | 1 | var CharacterSet = require('../match/CharacterSet'); |
7 | 1 | var Token = require('../Token'); |
8 | 1 | var Lexer = require('../Lexer'); |
9 | 1 | var character = require('../../util/character'); |
10 | 1 | var EcmascriptRule = Rule.extend(function() { |
11 | 272 | var self = this; |
12 | 272 | Rule.call(self, EcmascriptRule.KEYWORDS, true); |
13 | | |
14 | 272 | self.addMatch(new CompleteEqual(Token.BLANK, character.BLANK)); |
15 | 272 | self.addMatch(new CompleteEqual(Token.TAB, character.TAB)); |
16 | 272 | self.addMatch(new CompleteEqual(Token.LINE, character.ENTER + character.LINE)); |
17 | 272 | self.addMatch(new CompleteEqual(Token.LINE, character.ENTER)); |
18 | 272 | self.addMatch(new CompleteEqual(Token.LINE, character.LINE)); |
19 | | |
20 | 272 | self.addMatch(new LineSearch(Token.COMMENT, '//', [character.ENTER + character.LINE, character.ENTER, character.LINE])); |
21 | 272 | self.addMatch(new LineSearch(Token.COMMENT, '/*', '*/', true)); |
22 | 272 | self.addMatch(new LineParse(Token.STRING, '"', '"', false, Lexer.IS_REG)); |
23 | 272 | self.addMatch(new LineParse(Token.STRING, "'", "'", false, Lexer.IS_REG)); |
24 | 272 | self.addMatch(new LineParse(Token.TEMPLATE, '`', '`', true, Lexer.IS_REG)); |
25 | | |
26 | 272 | self.addMatch(new RegMatch(Token.ID, /^[$a-zA-Z_][$\w]*/, Lexer.SPECIAL, function() { |
27 | 96102 | return !!(self.keyWords().hasOwnProperty(this.content())); |
28 | | }, function() { |
29 | 96102 | return ['if', 'for', 'while'].indexOf(this.content()) != -1; |
30 | | })); |
31 | | |
32 | 272 | self.addMatch(new RegMatch(Token.NUMBER, /^\.\d+(?:E[+-]?\d*)?/i, { |
33 | | 'SyntaxError: missing exponent': /E[+-]?$/i |
34 | | }, Lexer.NOT_REG)); |
35 | | |
36 | 272 | self.addMatch(new CompleteEqual(Token.SIGN, ']', Lexer.NOT_REG)); |
37 | | |
38 | 272 | ['*=', '/=', '+=', '-=', '%=', '^=', '&=', '|=', '&&', '--', '++', '===', '==', '!==', '!=', '||', '>>>=', '<<<=', '<<<', '>>>', '>>=', '<<=', '<<', '>>', '>=', '<=', '...', '?:', '=>'].forEach(function(o) { |
39 | 7888 | self.addMatch(new CompleteEqual(Token.SIGN, o, Lexer.IS_REG)); |
40 | | }); |
41 | 272 | self.addMatch(new CharacterSet(Token.SIGN, ':;/?.,[]{}~!^|%=-+*()~><&\\', Lexer.IS_REG)); |
42 | | |
43 | 272 | self.addMatch(new RegMatch(Token.NUMBER, /^0x[\da-f]*/i, { |
44 | | "SyntaxError: missing hexadecimal digits after '0x'": /^0x$/i |
45 | | }, Lexer.NOT_REG)); |
46 | 272 | self.addMatch(new RegMatch(Token.NUMBER, /^\d+\.?\d*(?:E[+-]?\d*)?/i, { |
47 | | 'SyntaxError: missing exponent': /E[+-]?$/i |
48 | | }, Lexer.NOT_REG)); |
49 | | |
50 | 272 | self.addMatch(new CompleteEqual(Token.LINE, '\u2028')); |
51 | 272 | self.addMatch(new CompleteEqual(Token.LINE, '\u2029')); |
52 | 272 | self.addMatch(new CharacterSet(Token.BLANK, '\f\u000b\u00A0\uFEFF\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000')); |
53 | | }).statics({ |
54 | | KEYWORDS: 'boolean break case catch class const continue debugger default delete do else enum export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield'.split(' ') |
55 | | }); |
56 | 1 | module.exports = EcmascriptRule; |
/Users/army/Sites/homunculus/src/lexer/rule/Rule.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../../util/Class'); |
2 | 1 | var Rule = Class(function(words, pReg) { |
3 | 274 | var self = this; |
4 | 274 | self.kw = {}; |
5 | 274 | words.forEach(function(o) { |
6 | 12882 | self.kw[o] = true; |
7 | | }); |
8 | 274 | self.pReg = pReg || false; |
9 | 274 | self.matchList = []; |
10 | | }).methods({ |
11 | | perlReg: function() { |
12 | 232 | return this.pReg; |
13 | | }, |
14 | | addMatch: function(match) { |
15 | 13220 | this.matchList.push(match); |
16 | 13220 | return this; |
17 | | }, |
18 | | matches: function() { |
19 | 454319 | return this.matchList; |
20 | | }, |
21 | | keyWords: function() { |
22 | 192204 | return this.kw; |
23 | | } |
24 | | }); |
25 | 1 | module.exports = Rule; |
/Users/army/Sites/homunculus/src/parser/js/Context.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../../util/Class'); |
2 | 1 | var JsNode = require('./Node'); |
3 | 1 | var Token = require('../../lexer/Token'); |
4 | 1 | var Parser = require('./Parser'); |
5 | 1 | var id = 0; |
6 | 1 | var Context = Class(function(parent, name) { |
7 | 65 | this.id = id++; |
8 | 65 | this.parser = new Parser(); |
9 | 65 | this.parent = parent || null; //父上下文,如果是全局则为空 |
10 | 65 | this.name = name || null; //上下文名称,即函数名,函数表达式为空,全局也为空 |
11 | 65 | this.children = []; //函数声明或函数表达式所产生的上下文 |
12 | 65 | this.childrenMap = Object.create(null); //键是函数名,值是上下文,匿名函数表达式键为cid |
13 | 65 | this.vars = []; //变量var声明 |
14 | 65 | this.varsMap = Object.create(null); //键为id字面量,值是它的token的节点 |
15 | 65 | this.vardeclMap = Object.create(null); //var赋值记录,优先级vardecl > fndecl > varnodecl |
16 | 65 | this.params = []; //形参,函数上下文才有,即全局无 |
17 | 65 | this.paramsMap = Object.create(null); //键为id字面量,值是它的token的节点 |
18 | 65 | this.aParams = []; //实参,函数表达式才有 |
19 | 65 | this.vids = []; //上下文环境里用到的变量id |
20 | 65 | this.vidsMap = Object.create(null); //键为id字面量,值是它的token的节点 |
21 | 65 | this.returns = []; //上下文环境里return语句 |
22 | 65 | this.node = null; //对应的ast的节点 |
23 | 65 | this.thisIs = null; //this指向,仅函数表达式call或apply执行时有用 |
24 | 65 | if(!this.isTop()) { |
25 | 26 | this.parent.addChild(this); |
26 | | } |
27 | | }).methods({ |
28 | | parse: function(code) { |
29 | 35 | var ast; |
30 | 35 | if(code instanceof JsNode) { |
31 | 1 | ast = code; |
32 | | } |
33 | | else { |
34 | 34 | ast = this.parser.parse(code); |
35 | | } |
36 | 35 | recursion(ast, this); |
37 | 35 | return this; |
38 | | }, |
39 | | getId: function() { |
40 | 29 | return this.id; |
41 | | }, |
42 | | getName: function() { |
43 | 27 | return this.name; |
44 | | }, |
45 | | getParent: function() { |
46 | 2 | return this.parent; |
47 | | }, |
48 | | isTop: function() { |
49 | 66 | return !this.parent; |
50 | | }, |
51 | | isFnexpr: function() { |
52 | 1 | return !this.isTop() && !this.name; |
53 | | }, |
54 | | hasParam: function(p) { |
55 | 6 | return p in this.paramsMap; |
56 | | }, |
57 | | getParams: function() { |
58 | 1 | return this.params; |
59 | | }, |
60 | | addParam: function(p) { |
61 | | //形参不可能重复,无需判断 |
62 | 10 | this.paramsMap[p] = this.params.length; |
63 | 10 | this.params.push(p); |
64 | 10 | return this; |
65 | | }, |
66 | | getAParams: function() { |
67 | 4 | return this.aParams; |
68 | | }, |
69 | | addAParam: function(ap) { |
70 | 9 | this.aParams.push(ap); |
71 | 9 | return this; |
72 | | }, |
73 | | getChild: function(name) { |
74 | 7 | return this.childrenMap[name]; |
75 | | }, |
76 | | getChildren: function() { |
77 | 15 | return this.children; |
78 | | }, |
79 | | //通过name查找函数声明,id查找表达式 |
80 | | hasChild: function(name) { |
81 | 48 | return name in this.childrenMap; |
82 | | }, |
83 | | addChild: function(child) { |
84 | 26 | var name = child.getName(); |
85 | | //函数表达式名字为空用id删除 |
86 | 26 | if(name) { |
87 | 12 | if(name in this.vardeclMap) { |
88 | 1 | return this; |
89 | | } |
90 | 11 | this.delVar(name); |
91 | 11 | this.delChild(name); |
92 | | } |
93 | | else { |
94 | 14 | this.delChild(child.getId()); |
95 | | } |
96 | 25 | name = name || child.getId(); |
97 | 25 | this.childrenMap[name] = child; |
98 | 25 | this.children.push(child); |
99 | 25 | return this; |
100 | | }, |
101 | | //name函数声明,id表达式 |
102 | | delChild: function(name) { |
103 | 38 | if(this.hasChild(name)) { |
104 | 1 | var i = this.children.indexOf(this.childrenMap[name]); |
105 | 1 | this.children.splice(i, 1); |
106 | 1 | delete this.childrenMap[name]; |
107 | | } |
108 | 38 | return this; |
109 | | }, |
110 | | hasVar: function(v) { |
111 | 38 | return v in this.varsMap; |
112 | | }, |
113 | | addVar: function(node, assign) { |
114 | 17 | var v = node.leaves()[0].token().content(); |
115 | | //赋值拥有最高优先级,会覆盖掉之前的函数声明和var |
116 | 17 | if(assign) { |
117 | 13 | this.delVar(v); |
118 | 13 | this.delChild(v); |
119 | 13 | this.vardeclMap[v] = true; |
120 | | } |
121 | | //仅仅是var声明无赋值,且已有过声明或函数,忽略之 |
122 | 4 | else if(this.hasVar(v) || this.hasChild(v)) { |
123 | 2 | return this; |
124 | | } |
125 | 15 | this.varsMap[v] = node; |
126 | 15 | this.vars.push(node); |
127 | 15 | return this; |
128 | | }, |
129 | | delVar: function(v) { |
130 | 24 | if(this.hasVar(v)) { |
131 | 2 | var i = this.vars.indexOf(this.varsMap[v]); |
132 | 2 | this.vars.splice(i, 1); |
133 | 2 | delete this.varsMap[v]; |
134 | | } |
135 | 24 | return this; |
136 | | }, |
137 | | getVars: function() { |
138 | 4 | return this.vars; |
139 | | }, |
140 | | addReturn: function(node) { |
141 | 5 | this.returns.push(node); |
142 | 5 | return this; |
143 | | }, |
144 | | getReturns: function() { |
145 | 4 | return this.returns; |
146 | | }, |
147 | | hasVid: function(v) { |
148 | 8 | return v in this.vidsMap; |
149 | | }, |
150 | | getVid: function(v) { |
151 | 1 | return this.vidsMap[v]; |
152 | | }, |
153 | | addVid: function(node) { |
154 | 18 | var v = node.token().content(); |
155 | 18 | this.vids.push(node); |
156 | 18 | this.vidsMap[v] = this.vidsMap[v] || []; |
157 | 18 | this.vidsMap[v].push(node); |
158 | 18 | return this; |
159 | | }, |
160 | | getVids: function() { |
161 | 2 | return this.vids; |
162 | | }, |
163 | | getNode: function() { |
164 | 1 | return this.node; |
165 | | }, |
166 | | setNode: function(n) { |
167 | 26 | this.node = n; |
168 | 26 | return this; |
169 | | }, |
170 | | setThis: function(t) { |
171 | 4 | this.thisIs = t; |
172 | 4 | return this; |
173 | | }, |
174 | | getThis: function() { |
175 | 3 | return this.thisIs; |
176 | | } |
177 | | }); |
178 | | |
179 | 1 | function recursion(node, context) { |
180 | 668 | var isToken = node.name() == JsNode.TOKEN; |
181 | 668 | var isVirtual = isToken && node.token().type() == Token.VIRTUAL; |
182 | 668 | if(isToken) { |
183 | 385 | if(!isVirtual) { |
184 | 379 | var token = node.token(); |
185 | 379 | var s = token.content(); |
186 | 379 | if(s == 'return') { |
187 | 5 | context.addReturn(node); |
188 | | } |
189 | | } |
190 | | } |
191 | | else { |
192 | 283 | if(node.name() == JsNode.VARDECL) { |
193 | 17 | vardecl(node, context); |
194 | | } |
195 | 266 | else if(node.name() == JsNode.FNDECL) { |
196 | 12 | context = fndecl(node, context); |
197 | | } |
198 | 254 | else if(node.name() == JsNode.FNEXPR) { |
199 | 14 | context = fnexpr(node, context); |
200 | | } |
201 | 240 | else if(node.name() == JsNode.PRMREXPR) { |
202 | 52 | prmrexpr(node, context); |
203 | | } |
204 | 283 | node.leaves().forEach(function(leaf, i) { |
205 | 633 | recursion(leaf, context); |
206 | | }); |
207 | | } |
208 | | } |
209 | 1 | function vardecl(node, context) { |
210 | 17 | var leaves = node.leaves(); |
211 | 17 | var assign = !!leaves[1]; |
212 | 17 | context.addVar(node, assign); |
213 | | } |
214 | 1 | function fndecl(node, context) { |
215 | 12 | var v = node.leaves()[1].leaves().content(); |
216 | 12 | var child = new Context(context, v); |
217 | 12 | child.setNode(node); |
218 | 12 | var params = node.leaves()[3]; |
219 | 12 | if(params.name() == JsNode.FNPARAMS) { |
220 | 1 | addParam(params, child); |
221 | | } |
222 | 12 | return child; |
223 | | } |
224 | 1 | function fnexpr(node, context) { |
225 | | //函数表达式name为空 |
226 | 14 | var child = new Context(context); |
227 | 14 | child.setNode(node); |
228 | | //记录形参 |
229 | 14 | var params; |
230 | 14 | var v = node.leaves()[1]; |
231 | 14 | if(v.token().content() != '(') { |
232 | 6 | params = node.leaves()[3]; |
233 | | } |
234 | | else { |
235 | 8 | params = node.leaves()[2]; |
236 | | } |
237 | 14 | if(params.name() == JsNode.FNPARAMS) { |
238 | 7 | addParam(params, child); |
239 | | } |
240 | | //匿名函数检查实参传入情况,包括call和apply设置this |
241 | 14 | var next = node.next(); |
242 | | //!function(){}()形式 |
243 | 14 | if(next && next.name() == JsNode.ARGS) { |
244 | 7 | var leaves = next.leaves(); |
245 | | //长度2为()空参数,长度3有参数,第2个节点 |
246 | 7 | if(leaves.length == 3) { |
247 | 1 | addAParam(leaves[1], child); |
248 | | } |
249 | | } |
250 | | //call或applay |
251 | 7 | else if(next |
252 | | && next.name() == JsNode.TOKEN |
253 | | && next.token().content() == '.' |
254 | | && next.next() |
255 | | && next.next().name() == JsNode.TOKEN |
256 | | && ['call', 'apply'].indexOf(next.next().token().content()) > -1) { |
257 | 2 | var mmb = node.parent(); |
258 | 2 | if(mmb.name() == JsNode.MMBEXPR) { |
259 | 2 | var callexpr = mmb.parent(); |
260 | 2 | if(callexpr.name() == JsNode.CALLEXPR) { |
261 | 2 | var isApply = next.next().token().content() == 'apply'; |
262 | 2 | next = mmb.next(); |
263 | 2 | if(next && next.name() == JsNode.ARGS) { |
264 | 2 | var leaves = next.leaves(); |
265 | | //长度2为()空参数,长度3有参数,第2个节点 |
266 | 2 | if(leaves.length == 3) { |
267 | 2 | isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child); |
268 | | } |
269 | | } |
270 | | } |
271 | | } |
272 | | } |
273 | | //(function(){})()形式 |
274 | | else { |
275 | 5 | var prmr = node.parent(); |
276 | 5 | var prev = node.prev(); |
277 | 5 | if(prmr.name() == JsNode.PRMREXPR |
278 | | && prev |
279 | | && prev.name() == JsNode.TOKEN |
280 | | && prev.token().content() == '(') { |
281 | 5 | next = prmr.next(); |
282 | 5 | if(next && next.name() == JsNode.ARGS) { |
283 | 3 | var leaves = next.leaves(); |
284 | | //长度2为()空参数,长度3有参数,第2个节点 |
285 | 3 | if(leaves.length == 3) { |
286 | 1 | addAParam(leaves[1], child); |
287 | | } |
288 | | } |
289 | | //(function(){}).call()形式 |
290 | 2 | else if(next |
291 | | && next.name() == JsNode.TOKEN |
292 | | && next.token().content() == '.' |
293 | | && next.next() |
294 | | && next.next().name() == JsNode.TOKEN |
295 | | && ['call', 'apply'].indexOf(next.next().token().content()) > -1) { |
296 | 2 | var mmb = prmr.parent(); |
297 | 2 | if(mmb.name() == JsNode.MMBEXPR) { |
298 | 2 | var callexpr = mmb.parent(); |
299 | 2 | if(callexpr.name() == JsNode.CALLEXPR) { |
300 | 2 | var isApply = next.next().token().content() == 'apply'; |
301 | 2 | next = mmb.next(); |
302 | 2 | if(next && next.name() == JsNode.ARGS) { |
303 | 2 | var leaves = next.leaves(); |
304 | | //长度2为()空参数,长度3有参数,第2个节点 |
305 | 2 | if(leaves.length == 3) { |
306 | 2 | isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child); |
307 | | } |
308 | | else { |
309 | 0 | child.setThis(undefined); |
310 | | } |
311 | | } |
312 | | } |
313 | | } |
314 | | } |
315 | | } |
316 | | } |
317 | 14 | return child; |
318 | | } |
319 | | //支持es6 |
320 | 1 | function addParam(params, child) { |
321 | 8 | params.leaves().forEach(function(leaf, i) { |
322 | 13 | if(leaf.name() == JsNode.TOKEN && leaf.token().content() != ',') { |
323 | 9 | child.addParam(leaf.token().content()); |
324 | | } |
325 | 4 | else if(leaf.name() == JsNode.RESTPARAM) { |
326 | 1 | child.addParam(leaf.leaves()[1].token().content()); |
327 | | } |
328 | | }); |
329 | | } |
330 | 1 | function addAParam(params, child) { |
331 | 2 | params.leaves().forEach(function(leaf, i) { |
332 | 6 | if(i % 2 == 0) { |
333 | 4 | child.addAParam(leaf); |
334 | | } |
335 | | }); |
336 | | } |
337 | 1 | function addCallAParam(params, child) { |
338 | 2 | params.leaves().forEach(function(leaf, i) { |
339 | 6 | if(i == 0) { |
340 | 2 | child.setThis(leaf); |
341 | | } |
342 | 4 | else if(i % 2 == 1) { |
343 | 2 | child.addAParam(leaf); |
344 | | } |
345 | | }); |
346 | | } |
347 | 1 | function addApplyAParam(params, child) { |
348 | 2 | child.setThis(params.leaves()[0]); |
349 | 2 | if(params.leaves()[2]) { |
350 | 2 | params.leaves()[2].leaves()[0].leaves().forEach(function(leaf, i) { |
351 | 8 | if(i % 2 == 1) { |
352 | 3 | child.addAParam(leaf); |
353 | | } |
354 | | }); |
355 | | } |
356 | | } |
357 | 1 | function prmrexpr(node, context) { |
358 | 52 | var first = node.leaves()[0]; |
359 | 52 | if(first.name() == JsNode.TOKEN) { |
360 | 48 | var token = first.token(); |
361 | 48 | if(token.type() == Token.ID || token.content() == 'this') { |
362 | 18 | context.addVid(first); |
363 | | } |
364 | | } |
365 | | } |
366 | | |
367 | 1 | module.exports = Context; |
/Users/army/Sites/homunculus/src/parser/js/Node.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../../util/Class'); |
2 | 1 | var Node = Class(function(type, children) { |
3 | 770922 | this.type = type; |
4 | 770922 | if(type == Node.TOKEN) { |
5 | 140302 | this.children = children; |
6 | | } |
7 | 630620 | else if(Array.isArray(children)) { |
8 | 0 | this.children = children; |
9 | | } |
10 | | else { |
11 | 630620 | this.children = children ? [children] : []; |
12 | | } |
13 | 770922 | this.p = null; |
14 | 770922 | this.pr = null; |
15 | 770922 | this.ne = null; |
16 | 770922 | return this; |
17 | | }).methods({ |
18 | | name: function() { |
19 | 240875 | return this.type; |
20 | | }, |
21 | | leaves: function() { |
22 | 99014 | return this.children; |
23 | | }, |
24 | | leaf: function(i) { |
25 | 1 | return this.children[i]; |
26 | | }, |
27 | | number: function() { |
28 | 1 | return this.children.length; |
29 | | }, |
30 | | add: function() { |
31 | 169852 | var self = this; |
32 | 169852 | var args = Array.prototype.slice.call(arguments, 0); |
33 | 169852 | args.forEach(function(node) { |
34 | 238950 | node.parent(self); |
35 | 238950 | var last = self.children[self.children.length - 1]; |
36 | 238950 | if(last) { |
37 | 140129 | last.next(node); |
38 | 140129 | node.prev(last); |
39 | | } |
40 | 238950 | self.children.push(node); |
41 | | }); |
42 | 169852 | return self; |
43 | | }, |
44 | | token: function() { |
45 | 278901 | return this.children; |
46 | | }, |
47 | | parent: function(p) { |
48 | 238966 | if(p) { |
49 | 238950 | this.p = p; |
50 | | } |
51 | 238966 | return this.p; |
52 | | }, |
53 | | prev: function(pr) { |
54 | 140136 | if(pr) { |
55 | 140129 | this.pr = pr; |
56 | | } |
57 | 140136 | return this.pr; |
58 | | }, |
59 | | next: function(ne) { |
60 | 140169 | if(ne) { |
61 | 140129 | this.ne = ne; |
62 | | } |
63 | 140169 | return this.ne; |
64 | | } |
65 | | }).statics({ |
66 | | PROGRAM: 'program', |
67 | | ELEMS: 'elems', |
68 | | ELEM: 'elem', |
69 | | CSTSTMT: 'cststmt', |
70 | | LETSTMT: 'letstmt', |
71 | | VARSTMT: 'varstmt', |
72 | | VARDECL: 'vardecl', |
73 | | FNBODY: 'fnbody', |
74 | | BLOCK: 'block', |
75 | | ITERSTMT: 'iterstmt', |
76 | | TOKEN: 'token', |
77 | | FNPARAMS: 'fnparams', |
78 | | BINDELEMENT: 'bindelement', |
79 | | RESTPARAM: 'restparam', |
80 | | EXPR: 'expr', |
81 | | CLASSDECL: 'classdecl', |
82 | | CLASSTAIL: 'classtail', |
83 | | HERITAGE: 'heritage', |
84 | | CLASSBODY: 'classbody', |
85 | | METHOD: 'method', |
86 | | SUPERSTMT: 'superstmt', |
87 | | GETFN: 'getfn', |
88 | | SETFN: 'setfn', |
89 | | PROGRAM: 'program', |
90 | | STMT: 'stmt', |
91 | | ASSIGN: 'assign', |
92 | | EMPTSTMT: 'emptstmt', |
93 | | IFSTMT: 'ifstmt', |
94 | | CNTNSTMT: 'cntnstmt', |
95 | | BRKSTMT: 'brkstmt', |
96 | | RETSTMT: 'retstmt', |
97 | | WITHSTMT: 'withstmt', |
98 | | SWCHSTMT: 'swchstmt', |
99 | | CASEBLOCK: 'caseblock', |
100 | | CASECLAUSE: 'caseclause', |
101 | | DFTCLAUSE: 'dftclause', |
102 | | LABSTMT: 'labstmt', |
103 | | THRSTMT: 'thrstmt', |
104 | | TRYSTMT: 'trystmt', |
105 | | DEBSTMT: 'debstmt', |
106 | | EXPRSTMT: 'exprstmt', |
107 | | CACH: 'cach', |
108 | | FINL: 'finl', |
109 | | FNDECL: 'fndecl', |
110 | | FNEXPR: 'fnexpr', |
111 | | ASSIGNEXPR: 'assignexpr', |
112 | | CNDTEXPR: 'cndtexpr', |
113 | | LOGOREXPR: 'logorexpr', |
114 | | LOGANDEXPR: 'logandexpr', |
115 | | BITOREXPR: 'bitorexpr', |
116 | | BITANDEXPR: 'bitandexpr', |
117 | | BITXOREXPR: 'bitxorexpr', |
118 | | EQEXPR: 'eqexpr', |
119 | | RELTEXPR: 'reltexpr', |
120 | | SHIFTEXPR: 'shiftexpr', |
121 | | ADDEXPR: 'addexpr', |
122 | | MTPLEXPR: 'mtplexpr', |
123 | | UNARYEXPR: 'unaryexpr', |
124 | | MMBEXPR: 'mmbexpr', |
125 | | PRMREXPR: 'prmrexpr', |
126 | | ARRLTR: 'arrltr', |
127 | | OBJLTR: 'objltr', |
128 | | PROPTASSIGN: 'proptassign', |
129 | | PROPTNAME: 'proptname', |
130 | | PROPTSETS: 'propsets', |
131 | | ARGS: 'args', |
132 | | ARGLIST: 'arglist', |
133 | | IMPTSTMT: 'imptstmt', |
134 | | POSTFIXEXPR: 'postfixexpr', |
135 | | NEWEXPR: 'newexpr', |
136 | | CALLEXPR: 'callexpr', |
137 | | ARRBINDPAT: 'arrbindpat', |
138 | | OBJBINDPAT: 'objbindpat', |
139 | | BINDPROPT: 'bindpropt', |
140 | | SINGLENAME: 'singlename', |
141 | | BINDELEM: 'bindelem', |
142 | | BINDID: 'bindid', |
143 | | SPREAD: 'spread', |
144 | | getKey: function(s) { |
145 | 2 | if(!s) { |
146 | 1 | throw new Error('empty value'); |
147 | | } |
148 | 1 | if(!keys) { |
149 | 1 | var self = this; |
150 | 1 | keys = {}; |
151 | 1 | Object.keys(this).forEach(function(k) { |
152 | 81 | var v = self[k]; |
153 | 81 | keys[v] = k; |
154 | | }); |
155 | | } |
156 | 1 | return keys[s]; |
157 | | } |
158 | | }); |
159 | 1 | var keys; |
160 | 1 | module.exports = Node; |
/Users/army/Sites/homunculus/src/parser/js/Parser.js
Line | Hits | Source |
---|
1 | 1 | var Class = require('../../util/Class'); |
2 | 1 | var character = require('../../util/character'); |
3 | 1 | var Lexer = require('../../lexer/Lexer'); |
4 | 1 | var Rule = require('../../lexer/rule/EcmascriptRule'); |
5 | 1 | var Token = require('../../lexer/Token'); |
6 | 1 | var Node = require('./Node'); |
7 | 1 | var S = {}; |
8 | 1 | S[Token.BLANK] = S[Token.TAB] = S[Token.COMMENT] = S[Token.LINE] = S[Token.ENTER] = true; |
9 | 1 | var Parser = Class(function(lexer) { |
10 | 231 | this.init(lexer); |
11 | | }).methods({ |
12 | | parse: function(code) { |
13 | 196 | this.lexer.parse(code); |
14 | 196 | this.tree = this.program(); |
15 | 162 | return this.tree; |
16 | | }, |
17 | | ast: function() { |
18 | 4 | return this.tree; |
19 | | }, |
20 | | init: function(lexer) { |
21 | 232 | this.look = null; |
22 | 232 | this.tokens = null; |
23 | 232 | this.lastLine = 1; |
24 | 232 | this.lastCol = 1; |
25 | 232 | this.line = 1; |
26 | 232 | this.col = 1; |
27 | 232 | this.index = 0; |
28 | 232 | this.length = 0; |
29 | 232 | this.ignores = {}; |
30 | 232 | this.hasMoveLine = false; |
31 | 232 | this.tree = {}; |
32 | 232 | if(lexer) { |
33 | 166 | this.lexer = lexer; |
34 | | } |
35 | 66 | else if(this.lexer) { |
36 | 1 | this.lexer.init(); |
37 | | } |
38 | | else { |
39 | 65 | this.lexer = new Lexer(new Rule()); |
40 | | } |
41 | | }, |
42 | | program: function() { |
43 | 196 | this.tokens = this.lexer.tokens(); |
44 | 196 | this.length = this.tokens.length; |
45 | 196 | if(this.tokens.length) { |
46 | 194 | this.move(); |
47 | | } |
48 | 196 | var node = new Node(Node.PROGRAM); |
49 | 196 | while(this.look) { |
50 | 228 | node.add(this.element()); |
51 | | } |
52 | 162 | return node; |
53 | | }, |
54 | | element: function(allowSuper) { |
55 | 5396 | if(this.look.content() == 'function') { |
56 | 201 | return this.fndecl(); |
57 | | } |
58 | 5195 | else if(this.look.content() == 'class') { |
59 | 13 | return this.classdecl(); |
60 | | } |
61 | | else { |
62 | 5182 | return this.stmt(allowSuper); |
63 | | } |
64 | | }, |
65 | | stmt: function(allowSuper) { |
66 | 10868 | if(!this.look) { |
67 | 1 | this.error(); |
68 | | } |
69 | 10867 | switch(this.look.content()) { |
70 | | case 'let': |
71 | 3 | return this.letstmt(); |
72 | | case 'const': |
73 | 3 | return this.cststmt(); |
74 | | case 'var': |
75 | 1169 | return this.varstmt(); |
76 | | case '{': |
77 | 1791 | return this.block(); |
78 | | case ';': |
79 | 35 | return this.emptstmt(); |
80 | | case 'if': |
81 | 1692 | return this.ifstmt(); |
82 | | case 'do': |
83 | | case 'while': |
84 | | case 'for': |
85 | 458 | return this.iterstmt(); |
86 | | case 'continue': |
87 | 18 | return this.cntnstmt(); |
88 | | case 'break': |
89 | 123 | return this.brkstmt(); |
90 | | case 'return': |
91 | 1636 | return this.retstmt(); |
92 | | case 'with': |
93 | 1 | return this.withstmt(); |
94 | | case 'switch': |
95 | 8 | return this.swchstmt(); |
96 | | case 'throw': |
97 | 39 | return this.thrstmt(); |
98 | | case 'try': |
99 | 55 | return this.trystmt(); |
100 | | case 'debugger': |
101 | 1 | return this.debstmt(); |
102 | | case 'super': |
103 | 3 | if(!allowSuper) { |
104 | 1 | this.error('super must in a class'); |
105 | | } |
106 | 2 | return this.superstmt(); |
107 | | case 'import': |
108 | 0 | return this.imptstmt(); |
109 | | default: |
110 | 3832 | if(this.look.type() == Token.ID) { |
111 | 2944 | for(var i = this.index; i < this.length; i++) { |
112 | 3956 | var token = this.tokens[i]; |
113 | 3956 | if(!S[token.type()]) { |
114 | 2941 | if(token.content() == ':') { |
115 | 2 | return this.labstmt(); |
116 | | } |
117 | | else { |
118 | 2939 | return this.exprstmt(); |
119 | | } |
120 | | } |
121 | | } |
122 | | } |
123 | 891 | return this.exprstmt(); |
124 | | } |
125 | | }, |
126 | | exprstmt: function() { |
127 | 3830 | var node = new Node(Node.EXPRSTMT); |
128 | 3830 | node.add(this.expr(), this.match(';')); |
129 | 3821 | return node; |
130 | | }, |
131 | | cststmt: function(noSem) { |
132 | 3 | var node = new Node(Node.CSTSTMT); |
133 | 3 | node.add( |
134 | | this.match('const'), |
135 | | this.vardecl() |
136 | | ); |
137 | 3 | while(this.look && this.look.content() == ',') { |
138 | 1 | node.add( |
139 | | this.match(), |
140 | | this.vardecl() |
141 | | ); |
142 | | } |
143 | 3 | if(!noSem) { |
144 | 3 | node.add(this.match(';')); |
145 | | } |
146 | 3 | return node; |
147 | | }, |
148 | | letstmt: function(noSem) { |
149 | 3 | var node = new Node(Node.LETSTMT); |
150 | 3 | node.add( |
151 | | this.match('let'), |
152 | | this.vardecl() |
153 | | ); |
154 | 3 | while(this.look && this.look.content() == ',') { |
155 | 1 | node.add( |
156 | | this.match(), |
157 | | this.vardecl() |
158 | | ); |
159 | | } |
160 | 3 | if(!noSem) { |
161 | 3 | node.add(this.match(';')); |
162 | | } |
163 | 3 | return node; |
164 | | }, |
165 | | varstmt: function(noSem) { |
166 | 1253 | var node = new Node(Node.VARSTMT); |
167 | 1253 | node.add( |
168 | | this.match('var'), |
169 | | this.vardecl() |
170 | | ); |
171 | 1246 | while(this.look && this.look.content() == ',') { |
172 | 1555 | node.add( |
173 | | this.match(), |
174 | | this.vardecl() |
175 | | ); |
176 | | } |
177 | 1246 | if(!noSem) { |
178 | 1162 | node.add(this.match(';')); |
179 | | } |
180 | 1246 | return node; |
181 | | }, |
182 | | vardecl: function() { |
183 | 2816 | var node = new Node(Node.VARDECL); |
184 | 2816 | if(!this.look) { |
185 | 1 | this.error('missing variable name'); |
186 | | } |
187 | 2815 | if(['[', '{'].indexOf(this.look.content()) > -1) { |
188 | 10 | node.add(this.bindpat()); |
189 | 6 | if(!this.look || this.look.content() != '=') { |
190 | 1 | this.error('missing = in destructuring declaration'); |
191 | | } |
192 | 5 | node.add(this.assign()); |
193 | | } |
194 | | else { |
195 | 2805 | node.add(this.match(Token.ID, 'missing variable name')); |
196 | 2805 | if(this.look && this.look.content() == '=') { |
197 | 1919 | node.add(this.assign()); |
198 | | } |
199 | | } |
200 | 2809 | return node; |
201 | | }, |
202 | | bindpat: function() { |
203 | 15 | if(this.look.content() == '[') { |
204 | 8 | return this.arrbindpat(); |
205 | | } |
206 | 7 | else if(this.look.content() == '{') { |
207 | 7 | return this.objbindpat(); |
208 | | } |
209 | | }, |
210 | | arrbindpat: function() { |
211 | 8 | var node = new Node(Node.ARRBINDPAT); |
212 | 8 | node.add(this.match('[')); |
213 | 8 | while(this.look && this.look.content() != ']') { |
214 | 18 | if(this.look.content() == ',') { |
215 | 5 | node.add(this.match()); |
216 | | } |
217 | 13 | else if(this.look.content() == '...') { |
218 | 2 | break; |
219 | | } |
220 | | else { |
221 | 11 | node.add(this.bindelem()); |
222 | | } |
223 | | } |
224 | 8 | if(this.look.content() == '...') { |
225 | 2 | node.add(this.restparam()); |
226 | | } |
227 | 8 | node.add(this.match(']', 'missing ] after element list')); |
228 | 8 | return node; |
229 | | }, |
230 | | bindelem: function() { |
231 | 13 | var node = new Node(Node.BINDELEM); |
232 | 13 | if(['[', '{'].indexOf(this.look.content()) > -1) { |
233 | 5 | node.add(this.bindpat()); |
234 | 5 | if(this.look && this.look.content() == '=') { |
235 | 1 | node.add(this.assign()); |
236 | | } |
237 | | } |
238 | | else { |
239 | 8 | return this.singlename(); |
240 | | } |
241 | 5 | return node; |
242 | | }, |
243 | | singlename: function() { |
244 | 12 | var node = new Node(Node.SINGLENAME); |
245 | 12 | node.add(this.match(Token.ID)); |
246 | 12 | if(this.look && this.look.content() == '=') { |
247 | 4 | node.add(this.assign()); |
248 | | } |
249 | 12 | return node; |
250 | | }, |
251 | | objbindpat: function() { |
252 | 7 | var node = new Node(Node.OBJBINDPAT); |
253 | 7 | node.add(this.match('{')); |
254 | 7 | while(this.look && this.look.content() != '}') { |
255 | 8 | node.add(this.bindpropt()); |
256 | 6 | if(this.look && this.look.content() == ',') { |
257 | 2 | node.add(this.match()); |
258 | | } |
259 | | } |
260 | 5 | node.add(this.match('}', 'missing } after property list')); |
261 | 3 | return node; |
262 | | }, |
263 | | bindpropt: function() { |
264 | 8 | var node = new Node(Node.BINDPROPT); |
265 | 8 | switch(this.look.type()) { |
266 | | case Token.ID: |
267 | | case Token.STRING: |
268 | | case Token.NUMBER: |
269 | 7 | break; |
270 | | default: |
271 | 1 | this.error('invalid property id'); |
272 | | } |
273 | | //根据LL2分辨是PropertyName[?Yield, ?GeneratorParameter] : BindingElement[?Yield, ?GeneratorParameter] |
274 | | //还是SingleNameBinding [?Yield, ?GeneratorParameter] |
275 | 7 | for(var i = this.index; i < this.length; i++) { |
276 | 6 | var next = this.tokens[i]; |
277 | 6 | if(!S[next.tag()]) { |
278 | 6 | if(next.content() == ':') { |
279 | 2 | node.add(this.match(), this.match()); |
280 | 2 | node.add(this.bindelem()); |
281 | | } |
282 | | else { |
283 | 4 | node.add(this.singlename()); |
284 | | } |
285 | 6 | return node; |
286 | | } |
287 | | } |
288 | 1 | this.error('missing : after property id'); |
289 | | }, |
290 | | assign: function() { |
291 | 1929 | var node = new Node(Node.ASSIGN); |
292 | 1929 | node.add(this.match('=')); |
293 | 1929 | if(!this.look) { |
294 | 1 | this.error(); |
295 | | } |
296 | 1928 | node.add(this.assignexpr()); |
297 | 1928 | return node; |
298 | | }, |
299 | | block: function() { |
300 | 1906 | var node = new Node(Node.BLOCK); |
301 | 1906 | node.add(this.match('{')); |
302 | 1906 | while(this.look && this.look.content() != '}') { |
303 | 2988 | node.add(this.stmt()); |
304 | | } |
305 | 1906 | node.add(this.match('}', 'missing } in compound statement')); |
306 | 1906 | return node; |
307 | | }, |
308 | | emptstmt: function() { |
309 | 35 | var node = new Node(Node.EMPTSTMT); |
310 | 35 | node.add(this.match(';')); |
311 | 35 | return node; |
312 | | }, |
313 | | ifstmt: function() { |
314 | 1692 | var node = new Node(Node.IFSTMT); |
315 | 1692 | node.add( |
316 | | this.match('if'), |
317 | | this.match('('), |
318 | | this.expr(), |
319 | | this.match(')'), |
320 | | this.stmt() |
321 | | ); |
322 | 1691 | if(this.look && this.look.content() == 'else') { |
323 | 325 | node.add( |
324 | | this.match('else'), |
325 | | this.stmt() |
326 | | ); |
327 | | } |
328 | 1691 | return node; |
329 | | }, |
330 | | iterstmt: function() { |
331 | 458 | var node = new Node(Node.ITERSTMT); |
332 | 458 | switch(this.look.content()) { |
333 | | case 'do': |
334 | 9 | node.add( |
335 | | this.match(), |
336 | | this.stmt(), |
337 | | this.match('while'), |
338 | | this.match('('), |
339 | | this.expr(), |
340 | | this.match(')'), |
341 | | this.match(';') |
342 | | ); |
343 | 9 | break; |
344 | | case 'while': |
345 | 152 | node.add( |
346 | | this.match(), |
347 | | this.match('('), |
348 | | this.expr(), |
349 | | this.match(')'), |
350 | | this.stmt() |
351 | | ); |
352 | 152 | break; |
353 | | case 'for': |
354 | 297 | node.add( |
355 | | this.match(), |
356 | | this.match('(') |
357 | | ); |
358 | 297 | if(!this.look) { |
359 | 1 | this.error(); |
360 | | } |
361 | 296 | if(this.look.content() == 'var' || this.look.content() == 'let') { |
362 | 84 | var node2 = this.look.content() == 'var' ? this.varstmt(true) : this.letstmt(true); |
363 | 84 | if(!this.look) { |
364 | 1 | this.error('missing ; after for-loop initializer'); |
365 | | } |
366 | 83 | if(this.look.content() == 'in') { |
367 | 19 | if(node2.leaves().length > 2) { |
368 | 1 | this.error('invalid for/in left-hand side'); |
369 | | } |
370 | 18 | node.add(node2); |
371 | 18 | node.add( |
372 | | this.match(), |
373 | | this.expr() |
374 | | ); |
375 | | } |
376 | | else { |
377 | 64 | node.add(node2); |
378 | 64 | node.add(this.match(';')); |
379 | 64 | if(this.look.content() != ';') { |
380 | 63 | node.add(this.expr()); |
381 | | } |
382 | 63 | node.add(this.match(';')); |
383 | 63 | if(!this.look) { |
384 | 1 | this.error(); |
385 | | } |
386 | 62 | if(this.look.content() != ')') { |
387 | 59 | node.add(this.expr()); |
388 | | } |
389 | | } |
390 | | } |
391 | | else { |
392 | 212 | if(this.look.content() == 'in') { |
393 | 1 | this.error(); |
394 | | } |
395 | 211 | var hasIn = false; |
396 | 211 | for(var i = this.index; i < this.length; i++) { |
397 | 1924 | var t = this.tokens[i]; |
398 | 1924 | if(t.content() == 'in') { |
399 | 87 | hasIn = true; |
400 | 87 | break; |
401 | | } |
402 | 1837 | else if(t.content() == ')') { |
403 | 120 | break; |
404 | | } |
405 | | } |
406 | 211 | if(hasIn) { |
407 | 87 | node.add(this.expr(true), this.match('in'), this.expr()); |
408 | | } |
409 | | else { |
410 | 124 | if(this.look.content() != ';') { |
411 | 68 | node.add(this.expr()); |
412 | | } |
413 | | //for的;不能省略,强制判断 |
414 | 124 | if(!this.look || this.look.content() != ';') { |
415 | 1 | this.error('missing ;') |
416 | | } |
417 | 123 | node.add(this.match(';')); |
418 | 123 | if(!this.look) { |
419 | 1 | this.error(); |
420 | | } |
421 | 122 | if(this.look.content() != ';') { |
422 | 121 | node.add(this.expr()); |
423 | | } |
424 | 122 | if(!this.look || this.look.content() != ';') { |
425 | 1 | this.error('missing ;') |
426 | | } |
427 | 121 | node.add(this.match(';')); |
428 | 121 | if(!this.look) { |
429 | 1 | this.error(); |
430 | | } |
431 | 120 | if(this.look.content() != ')') { |
432 | 112 | node.add(this.expr()); |
433 | | } |
434 | | } |
435 | | } |
436 | 287 | node.add(this.match(')')); |
437 | 287 | node.add(this.stmt()); |
438 | | } |
439 | 448 | return node; |
440 | | }, |
441 | | cntnstmt: function() { |
442 | 18 | var node = new Node(Node.CNTNSTMT); |
443 | 18 | node.add(this.match('continue', true)); |
444 | 18 | if(this.look && this.look.type() == Token.ID) { |
445 | 1 | node.add(this.match()); |
446 | | } |
447 | 18 | node.add(this.match(';')); |
448 | 18 | return node; |
449 | | }, |
450 | | brkstmt: function() { |
451 | 123 | var node = new Node(Node.BRKSTMT); |
452 | 123 | node.add(this.match('break', true)); |
453 | 123 | if(this.look && this.look.type() == Token.ID) { |
454 | 1 | node.add(this.match()); |
455 | | } |
456 | 123 | node.add(this.match(';')); |
457 | 123 | return node; |
458 | | }, |
459 | | retstmt: function() { |
460 | 1636 | var node = new Node(Node.RETSTMT); |
461 | 1636 | node.add(this.match('return', true)); |
462 | | //return后换行视作省略;,包括多行注释的换行 |
463 | 1636 | if(this.look) { |
464 | 1635 | if(this.look.content() == ';' |
465 | | || this.look.content() == '}' |
466 | | || this.look.type() == Token.LINE |
467 | | || this.look.type() == Token.COMMENT) { |
468 | 82 | node.add(this.match(';')); |
469 | | } |
470 | | else { |
471 | 1553 | node.add(this.expr(), this.match(';')); |
472 | | } |
473 | | } |
474 | | else { |
475 | 1 | node.add(this.match(';')); |
476 | | } |
477 | 1636 | return node; |
478 | | }, |
479 | | withstmt: function() { |
480 | 1 | var node = new Node(Node.WITHSTMT); |
481 | 1 | node.add( |
482 | | this.match('with'), |
483 | | this.match('('), |
484 | | this.expr(), |
485 | | this.match(')'), |
486 | | this.stmt() |
487 | | ); |
488 | 1 | return node; |
489 | | }, |
490 | | swchstmt: function() { |
491 | 8 | var node = new Node(Node.SWCHSTMT); |
492 | 8 | node.add( |
493 | | this.match('switch'), |
494 | | this.match('('), |
495 | | this.expr(), |
496 | | this.match(')'), |
497 | | this.caseblock() |
498 | | ); |
499 | 7 | return node; |
500 | | }, |
501 | | caseblock: function() { |
502 | 8 | var node = new Node(Node.CASEBLOCK); |
503 | 8 | node.add(this.match('{')); |
504 | 8 | while(this.look && this.look.content() != '}') { |
505 | 102 | if(this.look.content() == 'case') { |
506 | 99 | node.add(this.caseclause()); |
507 | | } |
508 | 3 | else if(this.look.content() == 'default') { |
509 | 2 | node.add(this.dftclause()); |
510 | | } |
511 | | else { |
512 | 1 | this.error('invalid switch statement'); |
513 | | } |
514 | | } |
515 | 7 | node.add(this.match('}')); |
516 | 7 | return node; |
517 | | }, |
518 | | caseclause: function() { |
519 | 99 | var node = new Node(Node.CASECLAUSE); |
520 | 99 | node.add( |
521 | | this.match('case'), |
522 | | this.expr(), |
523 | | this.match(':') |
524 | | ); |
525 | 99 | while(this.look |
526 | | && this.look.content() != 'case' |
527 | | && this.look.content() != 'default' |
528 | | && this.look.content() != '}') { |
529 | 228 | node.add(this.stmt()); |
530 | | } |
531 | 99 | return node; |
532 | | }, |
533 | | dftclause: function() { |
534 | 2 | var node = new Node(Node.DFTCLAUSE); |
535 | 2 | node.add( |
536 | | this.match('default'), |
537 | | this.match(':') |
538 | | ); |
539 | 2 | while(this.look && this.look.content() != '}') { |
540 | 2 | node.add(this.stmt()); |
541 | | } |
542 | 2 | return node; |
543 | | }, |
544 | | labstmt: function() { |
545 | 2 | var node = new Node(Node.LABSTMT); |
546 | 2 | node.add( |
547 | | this.match(Token.ID), |
548 | | this.match(':'), |
549 | | this.stmt() |
550 | | ); |
551 | 2 | return node; |
552 | | }, |
553 | | thrstmt: function() { |
554 | 39 | var node = new Node(Node.THRSTMT); |
555 | 39 | node.add( |
556 | | this.match('throw', true), |
557 | | this.expr(), |
558 | | this.match(';') |
559 | | ); |
560 | 39 | return node; |
561 | | }, |
562 | | trystmt: function() { |
563 | 55 | var node = new Node(Node.TRYSTMT); |
564 | 55 | node.add( |
565 | | this.match('try'), |
566 | | this.block() |
567 | | ); |
568 | 55 | if(this.look && this.look.content() == 'catch') { |
569 | 54 | node.add(this.cach()); |
570 | 54 | if(this.look && this.look.content() == 'finally') { |
571 | 5 | node.add(this.finl()); |
572 | | } |
573 | | } |
574 | | else { |
575 | 1 | node.add(this.finl()); |
576 | | } |
577 | 55 | return node; |
578 | | }, |
579 | | debstmt: function() { |
580 | 1 | var node = new Node(Node.DEBSTMT); |
581 | 1 | node.add(this.match('debugger'), this.match(';')); |
582 | 1 | return node; |
583 | | }, |
584 | | cach: function() { |
585 | 54 | var node = new Node(Node.CACH); |
586 | 54 | node.add( |
587 | | this.match('catch'), |
588 | | this.match('('), |
589 | | this.match(Token.ID, 'missing identifier in catch'), |
590 | | this.match(')'), |
591 | | this.block() |
592 | | ); |
593 | 54 | return node; |
594 | | }, |
595 | | finl: function() { |
596 | 6 | var node = new Node(Node.FINL); |
597 | 6 | node.add( |
598 | | this.match('finally'), |
599 | | this.block() |
600 | | ); |
601 | 6 | return node; |
602 | | }, |
603 | | superstmt: function() { |
604 | 2 | var node = new Node(Node.SUPERSTMT); |
605 | 2 | node.add(this.match('super')); |
606 | 2 | if(!this.look) { |
607 | 0 | this.error(); |
608 | | } |
609 | 2 | if(this.look.content() == '.') { |
610 | 1 | while(this.look && this.look.content() == '.') { |
611 | 2 | node.add(this.match()); |
612 | 2 | if(!this.look) { |
613 | 0 | this.error(); |
614 | | } |
615 | 2 | if(this.look.content() == 'super') { |
616 | 1 | node.add(this.match()); |
617 | | } |
618 | | else { |
619 | 1 | break; |
620 | | } |
621 | | } |
622 | 1 | if(this.look.content() != '(') { |
623 | 1 | node.add(this.match(Token.ID)); |
624 | 1 | while(this.look && this.look.content() == '.') { |
625 | 0 | node.add(this.match(), this.match(Token.ID)); |
626 | | } |
627 | | } |
628 | | } |
629 | 2 | node.add( |
630 | | this.args(), |
631 | | this.match(';') |
632 | | ); |
633 | 2 | return node; |
634 | | }, |
635 | | imptstmt: function() { |
636 | 0 | var node = new Node(Node.IMPTSTMT); |
637 | 0 | return node; |
638 | | }, |
639 | | fndecl: function() { |
640 | 201 | var node = new Node(Node.FNDECL); |
641 | 201 | node.add( |
642 | | this.match('function'), |
643 | | this.match(Token.ID, 'function statement requires a name'), |
644 | | this.match('(') |
645 | | ); |
646 | 201 | if(!this.look) { |
647 | 1 | this.error('missing formal parameter'); |
648 | | } |
649 | 200 | if(this.look.content() != ')') { |
650 | 157 | node.add(this.fnparams()); |
651 | | } |
652 | 200 | node.add( |
653 | | this.match(')'), |
654 | | this.match('{'), |
655 | | this.fnbody(), |
656 | | this.match('}') |
657 | | ); |
658 | 200 | return node; |
659 | | }, |
660 | | fnexpr: function() { |
661 | 1625 | var node = new Node(Node.FNEXPR); |
662 | 1625 | node.add(this.match('function')); |
663 | 1625 | if(!this.look) { |
664 | 1 | this.error('missing formal parameter'); |
665 | | } |
666 | 1624 | if(this.look.type() == Token.ID) { |
667 | 22 | node.add(this.match()); |
668 | | } |
669 | 1624 | node.add(this.match('(')); |
670 | 1624 | if(!this.look) { |
671 | 1 | this.error(); |
672 | | } |
673 | 1623 | if(this.look.content() != ')') { |
674 | 1147 | node.add(this.fnparams()); |
675 | | } |
676 | 1621 | node.add( |
677 | | this.match(')'), |
678 | | this.match('{'), |
679 | | this.fnbody(), |
680 | | this.match('}', 'missing } in compound statement') |
681 | | ); |
682 | 1621 | return node; |
683 | | }, |
684 | | fnparams: function() { |
685 | 1305 | var node = new Node(Node.FNPARAMS); |
686 | 1305 | while(this.look && this.look.content() != ')' && this.look.content() != '...') { |
687 | 2286 | node.add(this.match(Token.ID, 'missing formal parameter')); |
688 | 2285 | if(this.look) { |
689 | 2285 | if(this.look.content() == ',') { |
690 | 983 | node.add(this.match()); |
691 | | } |
692 | 1302 | else if(this.look.content() == '=') { |
693 | 4 | node.add(this.bindelement()); |
694 | 4 | if(this.look && this.look.content() == ',') { |
695 | 3 | node.add(this.match()); |
696 | | } |
697 | | } |
698 | | } |
699 | | } |
700 | 1304 | if(!this.look) { |
701 | 1 | this.error('missing ) after formal parameters'); |
702 | | } |
703 | 1303 | if(this.look.content() == '...') { |
704 | 4 | node.add(this.restparam()); |
705 | | } |
706 | 1303 | return node; |
707 | | }, |
708 | | bindelement: function() { |
709 | 4 | var node = new Node(Node.BINDELEMENT); |
710 | 4 | node.add(this.match('='), this.assignexpr()); |
711 | 4 | return node; |
712 | | }, |
713 | | restparam: function() { |
714 | 6 | var node = new Node(Node.RESTPARAM); |
715 | 6 | node.add(this.match('...'), this.match(Token.ID)); |
716 | 6 | return node; |
717 | | }, |
718 | | fnbody: function(allowSuper) { |
719 | 1828 | var node = new Node(Node.FNBODY); |
720 | 1828 | while(this.look && this.look.content() != '}') { |
721 | 5168 | node.add(this.element(allowSuper)); |
722 | | } |
723 | 1828 | return node; |
724 | | }, |
725 | | classdecl: function() { |
726 | 13 | var node = new Node(Node.CLASSDECL); |
727 | 13 | node.add(this.match('class'), this.match(Token.ID)); |
728 | 13 | if(!this.look) { |
729 | 1 | this.error(); |
730 | | } |
731 | 12 | if(this.look.content() == 'extends') { |
732 | 3 | node.add(this.heratige()); |
733 | | } |
734 | 12 | node.add( |
735 | | this.match('{'), |
736 | | this.classbody(), |
737 | | this.match('}') |
738 | | ); |
739 | 9 | return node; |
740 | | }, |
741 | | heratige: function() { |
742 | 3 | var node = new Node(Node.HERITAGE); |
743 | 3 | node.add(this.match('extends'), this.match(Token.ID)); |
744 | 3 | return node; |
745 | | }, |
746 | | classbody: function() { |
747 | 12 | var node = new Node(Node.CLASSBODY), |
748 | | methods = {}, |
749 | | hasStatic = false; |
750 | 12 | while(this.look && this.look.content() != '}') { |
751 | 11 | if(this.look.content() == ';') { |
752 | 1 | node.add(this.match()); |
753 | 1 | continue; |
754 | | } |
755 | 10 | hasStatic = false; |
756 | 10 | if(this.look.content() == 'static') { |
757 | 2 | node.add(this.match()); |
758 | 2 | hasStatic = true; |
759 | | } |
760 | 10 | if(!this.look) { |
761 | 1 | this.error(); |
762 | | } |
763 | 9 | node.add(this.method(hasStatic, methods)); |
764 | | } |
765 | 9 | return node; |
766 | | }, |
767 | | method: function(hasStatic, methods, statics) { |
768 | 9 | var node = new Node(Node.METHOD); |
769 | 9 | if(this.look.content() == 'get') { |
770 | 1 | node.add(this.match(), this.getfn()); |
771 | | } |
772 | 8 | else if(this.look.content() == 'set') { |
773 | 1 | node.add(this.match(), this.setfn()); |
774 | | } |
775 | | else { |
776 | 7 | node.add(this.match(Token.ID)); |
777 | 7 | var id = node.leaves()[0].token().content(); |
778 | 7 | if(methods.hasOwnProperty(id)) { |
779 | 1 | this.error('duplicate method decl in class'); |
780 | | } |
781 | 6 | methods[id] = true; |
782 | 6 | node.add(this.match('(')); |
783 | 5 | if(this.look.content() != ')') { |
784 | 1 | node.add(this.fnparams()); |
785 | | } |
786 | 5 | node.add( |
787 | | this.match(')'), |
788 | | this.match('{'), |
789 | | this.fnbody(true), |
790 | | this.match('}', 'missing } in compound statement') |
791 | | ); |
792 | | } |
793 | 7 | return node; |
794 | | }, |
795 | | expr: function(noIn) { |
796 | 11059 | var node = new Node(Node.EXPR), |
797 | | assignexpr = this.assignexpr(noIn); |
798 | 11046 | if(this.look && this.look.content() == ',') { |
799 | 540 | node.add(assignexpr); |
800 | 540 | while(this.look && this.look.content() == ',') { |
801 | 1051 | node.add(this.match(), this.assignexpr(noIn)); |
802 | | } |
803 | | } |
804 | | else { |
805 | 10506 | return assignexpr; |
806 | | } |
807 | 540 | return node; |
808 | | }, |
809 | | assignexpr: function(noIn) { |
810 | 30787 | var node = new Node(Node.ASSIGNEXPR), |
811 | | cndt = this.cndtexpr(noIn); |
812 | 30774 | if(this.look && { |
813 | | '*=': true, |
814 | | '/=': true, |
815 | | '%=': true, |
816 | | '+=': true, |
817 | | '-=': true, |
818 | | '<<=': true, |
819 | | '>>=': true, |
820 | | '>>>=': true, |
821 | | '&=': true, |
822 | | '^=': true, |
823 | | '|=': true, |
824 | | '=': true |
825 | | }.hasOwnProperty(this.look.content())) { |
826 | 3675 | node.add(cndt, this.match(), this.assignexpr(noIn)); |
827 | | } |
828 | | else { |
829 | 27099 | return cndt; |
830 | | } |
831 | 3675 | return node; |
832 | | }, |
833 | | cndtexpr: function(noIn) { |
834 | 30787 | var node = new Node(Node.CNDTEXPR), |
835 | | logorexpr = this.logorexpr(noIn); |
836 | 30774 | if(this.look && this.look.content() == '?') { |
837 | 878 | node.add( |
838 | | logorexpr, |
839 | | this.match(), |
840 | | this.assignexpr(noIn), |
841 | | this.match(':'), |
842 | | this.assignexpr(noIn) |
843 | | ); |
844 | | } |
845 | | else { |
846 | 29896 | return logorexpr; |
847 | | } |
848 | 878 | return node; |
849 | | }, |
850 | | logorexpr: function(noIn) { |
851 | 30787 | var node = new Node(Node.LOGOREXPR), |
852 | | logandexpr = this.logandexpr(noIn); |
853 | 30774 | if(this.look && this.look.content() == '||') { |
854 | 1015 | node.add(logandexpr); |
855 | 1015 | while(this.look && this.look.content() == '||') { |
856 | 1150 | node.add( |
857 | | this.match(), |
858 | | this.logandexpr(noIn) |
859 | | ); |
860 | | } |
861 | | } |
862 | | else { |
863 | 29759 | return logandexpr; |
864 | | } |
865 | 1015 | return node; |
866 | | }, |
867 | | logandexpr: function(noIn) { |
868 | 31937 | var node = new Node(Node.LOGANDEXPR), |
869 | | bitorexpr = this.bitorexpr(noIn); |
870 | 31924 | if(this.look && this.look.content() == '&&') { |
871 | 1129 | node.add(bitorexpr); |
872 | 1129 | while(this.look && this.look.content() == '&&') { |
873 | 1375 | node.add( |
874 | | this.match(), |
875 | | this.bitorexpr(noIn) |
876 | | ); |
877 | | } |
878 | | } |
879 | | else { |
880 | 30795 | return bitorexpr; |
881 | | } |
882 | 1129 | return node; |
883 | | }, |
884 | | bitorexpr: function(noIn) { |
885 | 33312 | var node = new Node(Node.BITOREXPR), |
886 | | bitxorexpr = this.bitxorexpr(noIn); |
887 | 33299 | if(this.look && this.look.content() == '|') { |
888 | 5 | node.add(bitxorexpr); |
889 | 5 | while(this.look && this.look.content() == '|') { |
890 | 5 | node.add( |
891 | | this.match(), |
892 | | this.bitxorexpr(noIn) |
893 | | ); |
894 | | } |
895 | | } |
896 | | else { |
897 | 33294 | return bitxorexpr; |
898 | | } |
899 | 5 | return node; |
900 | | }, |
901 | | bitxorexpr: function(noIn) { |
902 | 33317 | var node = new Node(Node.BITXOREXPR), |
903 | | bitandexpr = this.bitandexpr(noIn); |
904 | 33304 | if(this.look && this.look.content() == '^') { |
905 | 2 | node.add(bitandexpr); |
906 | 2 | while(this.look && this.look.content() == '^') { |
907 | 2 | node.add( |
908 | | this.match(), |
909 | | this.bitandexpr(noIn) |
910 | | ); |
911 | | } |
912 | | } |
913 | | else { |
914 | 33302 | return bitandexpr; |
915 | | } |
916 | 2 | return node; |
917 | | }, |
918 | | bitandexpr: function(noIn) { |
919 | 33319 | var node = new Node(Node.BITANDEXPR), |
920 | | eqexpr = this.eqexpr(noIn); |
921 | 33306 | if(this.look && this.look.content() == '&') { |
922 | 18 | node.add(eqexpr); |
923 | 18 | while(this.look && this.look.content() == '&') { |
924 | 18 | node.add( |
925 | | this.match(), |
926 | | this.eqexpr(noIn) |
927 | | ); |
928 | | } |
929 | | } |
930 | | else { |
931 | 33288 | return eqexpr; |
932 | | } |
933 | 18 | return node; |
934 | | }, |
935 | | eqexpr: function(noIn) { |
936 | 33337 | var node = new Node(Node.EQEXPR), |
937 | | reltexpr = this.reltexpr(noIn); |
938 | 33324 | if(this.look && { |
939 | | '==': true, |
940 | | '===': true, |
941 | | '!==': true, |
942 | | '!=': true |
943 | | }.hasOwnProperty(this.look.content())) { |
944 | 1589 | node.add(reltexpr); |
945 | 1589 | while(this.look && { |
946 | | '==': true, |
947 | | '===': true, |
948 | | '!==': true, |
949 | | '!=': true |
950 | | }.hasOwnProperty(this.look.content())) { |
951 | 1590 | node.add( |
952 | | this.match(), |
953 | | this.reltexpr(noIn) |
954 | | ); |
955 | | } |
956 | | } |
957 | | else { |
958 | 31735 | return reltexpr; |
959 | | } |
960 | 1589 | return node; |
961 | | }, |
962 | | reltexpr: function(noIn) { |
963 | 34927 | var node = new Node(Node.RELTEXPR), |
964 | | shiftexpr = this.shiftexpr(); |
965 | 34914 | if(this.look && ({ |
966 | | '<': true, |
967 | | '>': true, |
968 | | '>=': true, |
969 | | '<=': true, |
970 | | 'instanceof': true |
971 | | }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) { |
972 | 477 | node.add(shiftexpr); |
973 | 477 | while(this.look && ({ |
974 | | '<': true, |
975 | | '>': true, |
976 | | '>=': true, |
977 | | '<=': true, |
978 | | 'instanceof': true |
979 | | }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) { |
980 | 477 | node.add( |
981 | | this.match(), |
982 | | this.shiftexpr() |
983 | | ); |
984 | | } |
985 | | } |
986 | | else { |
987 | 34437 | return shiftexpr; |
988 | | } |
989 | 477 | return node; |
990 | | }, |
991 | | shiftexpr: function() { |
992 | 35404 | var node = new Node(Node.SHIFTEXPR), |
993 | | addexpr = this.addexpr(); |
994 | 35391 | if(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) { |
995 | 8 | node.add(addexpr); |
996 | 8 | while(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) { |
997 | 9 | node.add( |
998 | | this.match(), |
999 | | this.addexpr() |
1000 | | ); |
1001 | | } |
1002 | | } |
1003 | | else { |
1004 | 35383 | return addexpr; |
1005 | | } |
1006 | 8 | return node; |
1007 | | }, |
1008 | | addexpr: function() { |
1009 | 35413 | var node = new Node(Node.ADDEXPR), |
1010 | | mtplexpr = this.mtplexpr(); |
1011 | 35400 | if(this.look && ['+', '-'].indexOf(this.look.content()) != -1) { |
1012 | 777 | node.add(mtplexpr); |
1013 | 777 | while(this.look && ['+', '-'].indexOf(this.look.content()) != -1) { |
1014 | 1318 | node.add( |
1015 | | this.match(), |
1016 | | this.mtplexpr() |
1017 | | ); |
1018 | | } |
1019 | | } |
1020 | | else { |
1021 | 34623 | return mtplexpr; |
1022 | | } |
1023 | 777 | return node; |
1024 | | }, |
1025 | | mtplexpr: function() { |
1026 | 36731 | var node = new Node(Node.MTPLEXPR), |
1027 | | unaryexpr = this.unaryexpr(); |
1028 | 36718 | if(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) { |
1029 | 51 | node.add(unaryexpr); |
1030 | 51 | while(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) { |
1031 | 52 | node.add( |
1032 | | this.match(), |
1033 | | this.unaryexpr() |
1034 | | ); |
1035 | | } |
1036 | | } |
1037 | | else { |
1038 | 36667 | return unaryexpr; |
1039 | | } |
1040 | 51 | return node; |
1041 | | }, |
1042 | | unaryexpr: function() { |
1043 | 38501 | var node = new Node(Node.UNARYEXPR); |
1044 | 38501 | if(!this.look) { |
1045 | 2 | this.error(); |
1046 | | } |
1047 | 38499 | switch(this.look.content()) { |
1048 | | case 'delete': |
1049 | | case 'void': |
1050 | | case 'typeof': |
1051 | | case '++': |
1052 | | case '--': |
1053 | | case '+': |
1054 | | case '-': |
1055 | | case '~': |
1056 | | case '!': |
1057 | 1718 | node.add( |
1058 | | this.match(), |
1059 | | this.unaryexpr() |
1060 | | ); |
1061 | 1716 | break; |
1062 | | default: |
1063 | 36781 | return this.postfixexpr(); |
1064 | | } |
1065 | 1716 | return node; |
1066 | | }, |
1067 | | postfixexpr: function() { |
1068 | 36781 | var node = new Node(Node.POSTFIXEXPR); |
1069 | 36781 | var leftexpr = this.leftexpr(); |
1070 | 36770 | if(this.look && ['++', '--'].indexOf(this.look.content()) > -1 && !this.hasMoveLine) { |
1071 | 327 | node.add(leftexpr); |
1072 | 327 | while(this.look && ['++', '--'].indexOf(this.look.content()) > -1) { |
1073 | 327 | node.add(this.match(undefined, true)); |
1074 | | } |
1075 | | } |
1076 | | else { |
1077 | 36443 | return leftexpr; |
1078 | | } |
1079 | 327 | return node; |
1080 | | }, |
1081 | | leftexpr: function() { |
1082 | 36781 | if(this.look.content() == 'new') { |
1083 | 190 | return this.newexpr(); |
1084 | | } |
1085 | | else { |
1086 | 36591 | return this.callexpr(); |
1087 | | } |
1088 | | }, |
1089 | | newexpr: function(depth) { |
1090 | 191 | depth = depth || 0; |
1091 | 191 | var node = new Node(Node.NEWEXPR); |
1092 | 191 | node.add(this.match('new')); |
1093 | 191 | if(!this.look) { |
1094 | 1 | this.error(); |
1095 | | } |
1096 | 190 | if(this.look.content() == 'new') { |
1097 | 1 | node.add(this.newexpr(depth + 1)); |
1098 | | } |
1099 | | else { |
1100 | 189 | node.add(this.mmbexpr()); |
1101 | | } |
1102 | 190 | if(this.look && this.look.content() == '(') { |
1103 | 179 | node.add(this.args()); |
1104 | | } |
1105 | 190 | if(this.look && ['.', '['].indexOf(this.look.content()) > -1) { |
1106 | 11 | var mmb = new Node(Node.MMBEXPR); |
1107 | 11 | mmb.add(node); |
1108 | 11 | while(this.look) { |
1109 | 20 | if(this.look.content() == '.') { |
1110 | 10 | mmb.add( |
1111 | | this.match(), |
1112 | | this.match(Token.ID) |
1113 | | ); |
1114 | | } |
1115 | 10 | else if(this.look.content() == '[') { |
1116 | 1 | mmb.add( |
1117 | | this.match(), |
1118 | | this.expr(), |
1119 | | this.match(']') |
1120 | | ); |
1121 | | } |
1122 | | else { |
1123 | 9 | break; |
1124 | | } |
1125 | | } |
1126 | 11 | if(depth == 0 && this.look && this.look.content() == '(') { |
1127 | 8 | var callexpr = this.callexpr(mmb); |
1128 | 8 | return callexpr; |
1129 | | } |
1130 | 3 | return mmb; |
1131 | | } |
1132 | 179 | return node; |
1133 | | }, |
1134 | | callexpr: function(mmb) { |
1135 | 36599 | var node = new Node(Node.CALLEXPR); |
1136 | 36599 | mmb = mmb || this.mmbexpr(); |
1137 | 36590 | if(this.look && this.look.content() == '(') { |
1138 | 5159 | node.add( |
1139 | | mmb, |
1140 | | this.args() |
1141 | | ); |
1142 | 5158 | if(this.look && ['.', '[', '('].indexOf(this.look.content()) > -1) { |
1143 | 364 | while(this.look) { |
1144 | 1186 | if(this.look.content() == '.') { |
1145 | 426 | node.add( |
1146 | | this.match(), |
1147 | | this.match(Token.ID) |
1148 | | ); |
1149 | | } |
1150 | 760 | else if(this.look.content() == '[') { |
1151 | 45 | node.add( |
1152 | | this.match(), |
1153 | | this.expr(), |
1154 | | this.match(']') |
1155 | | ); |
1156 | | } |
1157 | 715 | else if(this.look.content() == '(') { |
1158 | 351 | node.add(this.args()); |
1159 | | } |
1160 | | else { |
1161 | 364 | break; |
1162 | | } |
1163 | | } |
1164 | | } |
1165 | | } |
1166 | | else { |
1167 | 31431 | return mmb; |
1168 | | } |
1169 | 5158 | return node; |
1170 | | }, |
1171 | | mmbexpr: function() { |
1172 | 36780 | var node = new Node(Node.MMBEXPR); |
1173 | 36780 | var mmb; |
1174 | 36780 | if(this.look.content() == 'function') { |
1175 | 1625 | mmb = this.fnexpr(); |
1176 | | } |
1177 | | else { |
1178 | 35155 | mmb = this.prmrexpr(); |
1179 | | } |
1180 | 36771 | if(this.look && ['.', '['].indexOf(this.look.content()) > -1) { |
1181 | 10738 | node.add(mmb); |
1182 | 10738 | while(this.look) { |
1183 | 23521 | if(this.look.content() == '.') { |
1184 | 10860 | node.add( |
1185 | | this.match(), |
1186 | | this.match(Token.ID) |
1187 | | ); |
1188 | | } |
1189 | 12661 | else if(this.look.content() == '[') { |
1190 | 1928 | node.add( |
1191 | | this.match(), |
1192 | | this.expr(), |
1193 | | this.match(']') |
1194 | | ); |
1195 | | } |
1196 | | else { |
1197 | 10733 | break; |
1198 | | } |
1199 | | } |
1200 | | } |
1201 | | else { |
1202 | 26033 | return mmb; |
1203 | | } |
1204 | 10738 | return node; |
1205 | | }, |
1206 | | prmrexpr: function() { |
1207 | 35155 | var node = new Node(Node.PRMREXPR); |
1208 | 35155 | switch(this.look.type()) { |
1209 | | case Token.ID: |
1210 | | case Token.NUMBER: |
1211 | | case Token.STRING: |
1212 | | case Token.REG: |
1213 | | case Token.TEMPLATE: |
1214 | 29080 | node.add(this.match()); |
1215 | 29080 | break; |
1216 | | default: |
1217 | 6075 | switch(this.look.content()) { |
1218 | | case 'this': |
1219 | | case 'null': |
1220 | | case 'true': |
1221 | | case 'false': |
1222 | 3381 | node.add(this.match()); |
1223 | 3381 | break; |
1224 | | case '(': |
1225 | 1087 | node.add(this.match(), this.expr(), this.match(')')); |
1226 | 1083 | break; |
1227 | | case '[': |
1228 | 876 | node.add(this.arrltr()); |
1229 | 876 | break; |
1230 | | case '{': |
1231 | 730 | node.add(this.objltr()); |
1232 | 730 | break; |
1233 | | default: |
1234 | 1 | this.error(); |
1235 | | } |
1236 | | } |
1237 | 35150 | return node; |
1238 | | }, |
1239 | | bindid: function() { |
1240 | 2 | var node = new Node(Node.BINDID); |
1241 | 2 | node.add(this.match('...'), this.assignexpr()); |
1242 | 2 | return node; |
1243 | | }, |
1244 | | arrltr: function() { |
1245 | 876 | var node = new Node(Node.ARRLTR); |
1246 | 876 | node.add(this.match('[')); |
1247 | 876 | while(this.look && this.look.content() != ']' && this.look.content() != '...') { |
1248 | 1518 | if(this.look.content() == ',') { |
1249 | 4 | node.add(this.match()); |
1250 | | } |
1251 | | else { |
1252 | 1514 | node.add(this.assignexpr()); |
1253 | 1514 | if(this.look && this.look.content() == ',') { |
1254 | 871 | node.add(this.match()); |
1255 | | } |
1256 | | } |
1257 | | } |
1258 | 876 | if(this.look.content() == '...') { |
1259 | 1 | node.add(this.spread()); |
1260 | | } |
1261 | 876 | node.add(this.match(']', 'missing ] after element list')); |
1262 | 876 | return node; |
1263 | | }, |
1264 | | spread: function() { |
1265 | 1 | var node = new Node(Node.SPREAD); |
1266 | 1 | node.add(this.match('...'), this.assignexpr()); |
1267 | 1 | return node; |
1268 | | }, |
1269 | | objltr: function() { |
1270 | 730 | var node = new Node(Node.OBJLTR); |
1271 | 730 | node.add(this.match('{')); |
1272 | 730 | while(this.look && this.look.content() != '}') { |
1273 | 2002 | node.add(this.proptassign()); |
1274 | 2002 | if(this.look && this.look.content() == ',') { |
1275 | 1511 | node.add(this.match()); |
1276 | | } |
1277 | | } |
1278 | 730 | node.add(this.match('}', 'missing } after property list')); |
1279 | 730 | return node; |
1280 | | }, |
1281 | | proptassign: function() { |
1282 | 2002 | var node = new Node(Node.PROPTASSIGN); |
1283 | 2002 | if(!this.look) { |
1284 | 0 | this.error(); |
1285 | | } |
1286 | 2002 | if(this.look.content() == 'get') { |
1287 | 28 | node.add(this.match()); |
1288 | 28 | if(!this.look) { |
1289 | 0 | this.error(); |
1290 | | } |
1291 | 28 | if(this.look.content() == ':') { |
1292 | 28 | node.add(this.match(), this.assignexpr()); |
1293 | | } |
1294 | | else { |
1295 | 0 | node.add(this.getfn()); |
1296 | | } |
1297 | | } |
1298 | 1974 | else if(this.look.content() == 'set') { |
1299 | 30 | node.add(this.match()); |
1300 | 30 | if(!this.look) { |
1301 | 0 | this.error(); |
1302 | | } |
1303 | 30 | if(this.look.content() == ':') { |
1304 | 30 | node.add(this.match(), this.assignexpr()); |
1305 | | } |
1306 | | else { |
1307 | 0 | node.add(this.setfn()); |
1308 | | } |
1309 | | } |
1310 | | else { |
1311 | 1944 | switch(this.look.type()) { |
1312 | | case Token.ID: |
1313 | | case Token.STRING: |
1314 | | case Token.NUMBER: |
1315 | 1944 | node.add( |
1316 | | this.match(), |
1317 | | this.match(':', 'missing : after property id'), |
1318 | | this.assignexpr() |
1319 | | ); |
1320 | 1944 | break; |
1321 | | default: |
1322 | 0 | this.error('invalid property id'); |
1323 | | } |
1324 | | } |
1325 | 2002 | return node; |
1326 | | }, |
1327 | | getfn: function() { |
1328 | 1 | var node = new Node(Node.GETFN); |
1329 | 1 | node.add( |
1330 | | this.proptname(), |
1331 | | this.match('('), |
1332 | | this.match(')'), |
1333 | | this.match('{'), |
1334 | | this.fnbody(), |
1335 | | this.match('}') |
1336 | | ); |
1337 | 1 | return node; |
1338 | | }, |
1339 | | setfn: function() { |
1340 | 1 | var node = new Node(Node.SETFN); |
1341 | 1 | node.add( |
1342 | | this.proptname(), |
1343 | | this.match('('), |
1344 | | this.propsets(), |
1345 | | this.match(')'), |
1346 | | this.match('{'), |
1347 | | this.fnbody(), |
1348 | | this.match('}') |
1349 | | ); |
1350 | 1 | return node; |
1351 | | }, |
1352 | | proptname: function() { |
1353 | 2 | var node = new Node(Node.PROPTNAME); |
1354 | 2 | if(this.look) { |
1355 | 2 | switch(this.look.type()) { |
1356 | | case Token.ID: |
1357 | | case Token.NUMBER: |
1358 | | case Token.STRING: |
1359 | 2 | node.add(this.match()); |
1360 | 2 | break; |
1361 | | default: |
1362 | 0 | this.error('missing name after . operator'); |
1363 | | } |
1364 | | } |
1365 | 2 | return node; |
1366 | | }, |
1367 | | propsets: function() { |
1368 | 1 | var node = new Node(Node.PROPTSETS); |
1369 | 1 | node.add(this.match(Token.ID, 'setter functions must have one argument')); |
1370 | 1 | return node; |
1371 | | }, |
1372 | | args: function() { |
1373 | 5691 | var node = new Node(Node.ARGS); |
1374 | 5691 | node.add(this.match('(')); |
1375 | 5691 | if(!this.look) { |
1376 | 1 | this.error(); |
1377 | | } |
1378 | 5690 | if(this.look.content() != ')') { |
1379 | 4949 | node.add(this.arglist()); |
1380 | | } |
1381 | 5690 | node.add(this.match(')')); |
1382 | 5690 | return node; |
1383 | | }, |
1384 | | arglist: function() { |
1385 | 4949 | var node = new Node(Node.ARGLIST); |
1386 | 4949 | while(this.look && this.look.content() != ')' && this.look.content() != '...') { |
1387 | 7795 | node.add(this.assignexpr()); |
1388 | 7795 | if(this.look && this.look.content() == ',') { |
1389 | 2848 | node.add(this.match()); |
1390 | | } |
1391 | | } |
1392 | 4949 | if(this.look && this.look.content() == '...') { |
1393 | 2 | node.add(this.bindid()); |
1394 | | } |
1395 | 4949 | return node; |
1396 | | }, |
1397 | | virtual: function(s) { |
1398 | 1590 | return new Node(Node.TOKEN, new Token(Token.VIRTUAL, s)); |
1399 | | }, |
1400 | | match: function(type, line, msg) { |
1401 | 140306 | if(typeof type == 'boolean') { |
1402 | 0 | msg = line; |
1403 | 0 | line = type; |
1404 | 0 | type = undefined; |
1405 | | } |
1406 | 140306 | if(typeof line != 'boolean') { |
1407 | 138163 | line = false; |
1408 | 138163 | msg = line; |
1409 | | } |
1410 | | //未定义为所有非空白token |
1411 | 140306 | if(character.isUndefined(type)) { |
1412 | 70821 | if(this.look) { |
1413 | 70821 | var l = this.look; |
1414 | 70821 | this.move(line); |
1415 | 70821 | return new Node(Node.TOKEN, l); |
1416 | | } |
1417 | | else { |
1418 | 0 | this.error('syntax error' + (msg || '')); |
1419 | | } |
1420 | | } |
1421 | | //或者根据token的type或者content匹配 |
1422 | 69485 | else if(typeof type == 'string') { |
1423 | | //特殊处理;,不匹配但有换行或者末尾时自动补全,还有受限行 |
1424 | 52798 | if(type == ';' |
1425 | | && (!this.look |
1426 | | || (this.look.content() != type && this.hasMoveLine) |
1427 | | || this.look.content() == '}') |
1428 | | ) { |
1429 | 1590 | if(this.look && S[this.look.type()]) { |
1430 | 36 | this.move(); |
1431 | | } |
1432 | 1590 | return this.virtual(';'); |
1433 | | } |
1434 | 51208 | else if(this.look && this.look.content() == type) { |
1435 | 51205 | var l = this.look; |
1436 | 51205 | this.move(line); |
1437 | 51205 | return new Node(Node.TOKEN, l); |
1438 | | } |
1439 | | else { |
1440 | 3 | this.error('missing ' + type + (msg || '')); |
1441 | | } |
1442 | | } |
1443 | 16687 | else if(typeof type == 'number') { |
1444 | 16687 | if(this.look && this.look.type() == type) { |
1445 | 16686 | var l = this.look; |
1446 | 16686 | this.move(line); |
1447 | 16686 | return new Node(Node.TOKEN, l); |
1448 | | } |
1449 | | else { |
1450 | 1 | this.error('missing ' + Token.type(type) + (msg || '')); |
1451 | | } |
1452 | | } |
1453 | | }, |
1454 | | move: function(line) { |
1455 | 138942 | this.lastLine = this.line; |
1456 | 138942 | this.lastCol = this.col; |
1457 | | //遗留下来的换行符 |
1458 | 138942 | this.hasMoveLine = false; |
1459 | 138942 | do { |
1460 | 248200 | this.look = this.tokens[this.index++]; |
1461 | 248200 | if(!this.look) { |
1462 | 184 | return; |
1463 | | } |
1464 | | //存下忽略的token |
1465 | 248016 | if(S[this.look.type()]) { |
1466 | 109294 | this.ignores[this.index - 1] = this.look; |
1467 | | } |
1468 | | //包括line的情况下要跳出 |
1469 | 248016 | if(this.look.type() == Token.LINE) { |
1470 | 17784 | this.line++; |
1471 | 17784 | this.col = 1; |
1472 | 17784 | this.hasMoveLine = true; |
1473 | 17784 | if(line) { |
1474 | 35 | break; |
1475 | | } |
1476 | | } |
1477 | 230232 | else if(this.look.type() == Token.COMMENT) { |
1478 | 2742 | var s = this.look.content(); |
1479 | 2742 | var n = character.count(this.look.content(), character.LINE); |
1480 | 2742 | if(n > 0) { |
1481 | 90 | this.line += n; |
1482 | 90 | var i = s.lastIndexOf(character.LINE); |
1483 | 90 | this.col += s.length - i - 1; |
1484 | 90 | this.hasMoveLine = true; |
1485 | 90 | if(line) { |
1486 | 1 | break; |
1487 | | } |
1488 | | } |
1489 | | } |
1490 | | else { |
1491 | 227490 | this.col += this.look.content().length; |
1492 | 227490 | if(!S[this.look.type()]) { |
1493 | 138722 | break; |
1494 | | } |
1495 | | } |
1496 | | } while(this.index <= this.length); |
1497 | | }, |
1498 | | error: function(msg) { |
1499 | 33 | msg = 'SyntaxError: ' + (msg || ' syntax error'); |
1500 | 33 | throw new Error(msg + ' line ' + this.lastLine + ' col ' + this.lastCol); |
1501 | | }, |
1502 | | ignore: function() { |
1503 | 8 | return this.ignores; |
1504 | | } |
1505 | | }); |
1506 | 1 | module.exports = Parser; |
/Users/army/Sites/homunculus/src/util/Class.js
Line | Hits | Source |
---|
1 | 1 | function inheritPrototype(subType, superType) { |
2 | 10 | var prototype = Object.create(superType.prototype); |
3 | 10 | prototype.constructor = subType; |
4 | 10 | subType.prototype = prototype; |
5 | | //继承static变量 |
6 | 10 | Object.keys(superType).forEach(function(k) { |
7 | 37 | subType[k] = superType[k]; |
8 | | }); |
9 | 10 | return subType; |
10 | | } |
11 | 1 | function wrap(fn) { |
12 | 19 | fn.extend = function(sub) { |
13 | 10 | inheritPrototype(sub, fn); |
14 | 10 | return wrap(sub); |
15 | | } |
16 | 19 | fn.methods = function(o) { |
17 | 16 | Object.keys(o).forEach(function(k) { |
18 | 201 | fn.prototype[k] = o[k]; |
19 | | }); |
20 | 16 | return fn; |
21 | | }; |
22 | 19 | fn.statics = function(o) { |
23 | 8 | Object.keys(o).forEach(function(k) { |
24 | 143 | fn[k] = o[k]; |
25 | | }); |
26 | 8 | return fn; |
27 | | }; |
28 | 19 | return fn; |
29 | | } |
30 | 1 | function klass(cons) { |
31 | 9 | return wrap(cons || function() {}); |
32 | | } |
33 | 1 | klass.extend = inheritPrototype; |
34 | 1 | module.exports = klass; |
/Users/army/Sites/homunculus/src/util/character.js
Line | Hits | Source |
---|
1 | 1 | exports.LINE = '\n'; |
2 | 1 | exports.ENTER = '\r'; |
3 | 1 | exports.BLANK = ' '; |
4 | 1 | exports.TAB = '\t'; |
5 | 1 | exports.UNDERLINE = '_'; |
6 | 1 | exports.DOLLAR = '$'; |
7 | 1 | exports.SHARP = '#'; |
8 | 1 | exports.MINUS = '-'; |
9 | 1 | exports.AT = '@'; |
10 | 1 | exports.SLASH = '/'; |
11 | 1 | exports.BACK_SLASH = '\\'; |
12 | 1 | exports.DECIMAL = '.'; |
13 | 1 | exports.LEFT_BRACKET = '['; |
14 | 1 | exports.RIGHT_BRACKET = ']'; |
15 | 1 | exports.STAR = '*'; |
16 | 1 | exports.LEFT_PARENTHESE = '('; |
17 | 1 | exports.RIGHT_PARENTHESE = ')'; |
18 | 1 | exports.COMMA = ','; |
19 | 1 | exports.SEMICOLON = ';'; |
20 | 1 | exports.EQUAL = '='; |
21 | 1 | exports.isDigit = function(c) { |
22 | 0 | return c >= '0' && c <= '9'; |
23 | | }; |
24 | 1 | exports.isDigit16 = function(c) { |
25 | 0 | return exports.isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); |
26 | | }; |
27 | 1 | exports.isDigit2 = function(c) { |
28 | 0 | return c == '0' || c == '1'; |
29 | | }; |
30 | 1 | exports.isDigit8 = function(c) { |
31 | 0 | return c >= '0' && c <= '7'; |
32 | | }; |
33 | 1 | exports.isLetter = function(c) { |
34 | 691 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
35 | | }; |
36 | 1 | exports.count = function(s, c) { |
37 | 457060 | var count = 0, |
38 | | i = -1; |
39 | 457060 | while((i = s.indexOf(c, i + 1)) != -1) { |
40 | 33562 | count++; |
41 | | } |
42 | 457060 | return count; |
43 | | }; |
44 | 1 | exports.isUndefined = function(s) { |
45 | 4427744 | return typeof s == 'undefined'; |
46 | | }; |
47 | 1 | exports.isString = function(s) { |
48 | 0 | return Object.prototype.toString.call(s) == "[object String]"; |
49 | | }; |
50 | 1 | exports.isNumber = function(s) { |
51 | 456402 | return Object.prototype.toString.call(s) == "[object Number]"; |
52 | | }; |