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

98%
1391
1364
27

/Users/army/Sites/homunculus/src/lexer/Lexer.js

100%
128
128
0
LineHitsSource
11var Class = require('../util/Class');
21var character = require('../util/character');
31var Token = require('./Token');
41var Lexer = Class(function(rule) {
5274 this.rule = rule; //当前语法规则
6274 this.init();
7}).methods({
8 init: function() {
9275 this.code = ''; //要解析的代码
10275 this.peek = ''; //向前看字符
11275 this.index = 0; //向前看字符字符索引
12275 this.isReg = Lexer.IS_REG; //当前/是否是perl风格正则表达式
13275 this.tokenList = []; //结果的token列表
14275 this.parentheseState = false; //(开始时标记之前终结符是否为if/for/while等关键字
15275 this.parentheseStack = []; //圆括号深度记录当前是否为if/for/while等语句内部
16275 this.cacheLine = 0; //行缓存值
17275 this.totalLine = 1; //总行数
18275 this.colNum = 0; //列
19275 this.colMax = 0; //最大列数
20 },
21 parse: function(code) {
22231 this.code = code || '';
23231 var temp = [];
24231 this.scan(temp);
25222 return temp;
26 },
27 parseOn: function() {
281 var temp = [];
291 this.scan(temp);
301 return temp;
31 },
32 tokens: function() {
33196 return this.tokenList;
34 },
35 scan: function(temp) {
36232 var perlReg = this.rule.perlReg();
37232 var length = this.code.length;
38232 var count = 0;
39 outer:
40 while(this.index < length) {
41454813 if(this.cacheLine > 0 && count >= this.cacheLine) {
421 break;
43 }
44454812 this.readch();
45 //perl风格正则
46454812 if(perlReg && this.isReg == Lexer.IS_REG && this.peek == character.SLASH && !{ '/': true, '*': true }[this.code.charAt(this.index)]) {
47493 this.dealReg(temp, length);
48489 this.isReg = Lexer.NOT_REG;
49 }
50 //依次遍历匹配规则,命中则继续
51 else {
52454319 for(var i = 0, matches = this.rule.matches(), len = matches.length; i < len; i++) {
537888002 var match = matches[i];
547888002 if(match.match(this.peek, this.code, this.index)) {
55454318 var token = new Token(match.tokenType(), match.content(), match.val(), this.index - 1);
56454318 var error = match.error();
57454318 var matchLen = match.content().length;
58454318 if(token.type() == Token.ID && this.rule.keyWords().hasOwnProperty(token.content())) {
5921502 token.type(Token.KEYWORD);
60 }
61454318 temp.push(token);
62454318 this.tokenList.push(token);
63454318 this.index += matchLen - 1;
64454318 var n = character.count(token.val(), character.LINE);
65454318 count += n;
66454318 this.totalLine += n;
67454318 if(n) {
6832320 var j = match.content().indexOf(character.LINE);
6932320 var k = match.content().lastIndexOf(character.LINE);
7032320 this.colMax = Math.max(this.colMax, this.colNum + j);
7132320 this.colNum = match.content().length - k;
72 }
73 else {
74421998 this.colNum += matchLen;
75 }
76454318 this.colMax = Math.max(this.colMax, this.colNum);
77454318 if(error) {
785 this.error(error, this.code.slice(this.index - matchLen, this.index));
79 }
80 //支持perl正则需判断关键字、圆括号对除号语义的影响
81454314 if(perlReg && match.perlReg() != Lexer.IGNORE) {
82258495 if(match.perlReg() == Lexer.SPECIAL) {
8396102 this.isReg = match.special();
84 }
85 else {
86162393 this.isReg = match.perlReg();
87 }
88258495 if(this.peek == character.LEFT_PARENTHESE) {
8919892 this.parentheseStack.push(this.parentheseState);
9019892 this.parentheseState = false;
91 }
92238603 else if(this.peek == character.RIGHT_PARENTHESE) {
9319875 this.isReg = this.parentheseStack.pop() ? Lexer.IS_REG : Lexer.NOT_REG;
94 }
95 else {
96218728 this.parentheseState = match.parenthese();
97 }
98 }
99454314 continue outer;
100 }
101 }
102 //如果有未匹配的,说明规则不完整,抛出错误
1031 this.error('unknow token');
104 }
105 }
106223 return this;
107 },
108 readch: function() {
109463674 this.peek = this.code.charAt(this.index++);
110 },
111 dealReg: function(temp, length) {
112493 var lastIndex = this.index - 1;
113493 var res = false;
114 outer:
115 do {
1166803 this.readch();
1176803 if(this.peek == character.LINE) {
1182 this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index));
1191 break;
120 }
1216801 else if(this.peek == character.BACK_SLASH) {
122490 this.index++;
123 }
1246311 else if(this.peek == character.LEFT_BRACKET) {
125264 do {
1261368 this.readch();
1271368 if(this.peek == character.LINE) {
1282 this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index));
1291 break outer;
130 }
1311366 else if(this.peek == character.BACK_SLASH) {
132265 this.index++;
133 }
1341101 else if(this.peek == character.RIGHT_BRACKET) {
135262 continue outer;
136 }
137 } while(this.index < length);
138 }
1396047 else if(this.peek == character.SLASH) {
140488 res = true;
141488 var hash = {};
142488 var flag = {
143 'g': true,
144 'i': true,
145 'm': true,
146 'y': true
147 };
148 //正则的flag中有gimy4种,大小写敏感且不能重复
149488 do {
150691 this.readch();
151691 if(character.isLetter(this.peek)) {
152205 if(hash.hasOwnProperty(this.peek) || !flag.hasOwnProperty(this.peek)) {
1532 this.error('SyntaxError: invalid regular expression flag ' + this.peek, this.code.slice(lastIndex, this.index));
1541 break outer;
155 }
156203 hash[this.peek] = true;
157 }
158 else {
159486 break outer;
160 }
161 } while(this.index <= length);
162 }
163 } while(this.index < length);
164490 if(!res) {
1653 this.error('SyntaxError: unterminated regular expression literal', this.code.slice(lastIndex, this.index - 1));
166 }
167489 var token = new Token(Token.REG, this.code.slice(lastIndex, --this.index), lastIndex);
168489 temp.push(token);
169489 this.tokenList.push(token);
170489 this.colNum += this.index - lastIndex;
171489 this.colMax = Math.max(this.colMax, this.colNum);
172489 return this;
173 },
174 cache: function(i) {
1751 if(!character.isUndefined(i) && i !== null) {
1761 this.cacheLine = i;
177 }
1781 return this.cacheLine;
179 },
180 finish: function() {
1812 return this.index >= this.code.length;
182 },
183 line: function() {
18416 return this.totalLine;
185 },
186 col: function() {
1871 return this.colMax;
188 },
189 error: function(s, str) {
19015 if(character.isUndefined(str)) {
1911 str = this.code.substr(this.index - 1, 20);
192 }
19315 if(Lexer.mode() === Lexer.STRICT) {
1949 throw new Error(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str);
195 }
1966 else if(Lexer.mode() === Lexer.LOOSE && !character.isUndefined(console)) {
1976 if(console.warn) {
1986 console.warn(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str);
199 }
200 }
2016 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) {
21133 if(!character.isUndefined(i)) {
21210 cmode = i;
213 }
21433 return cmode;
215 }
216});
2171var cmode = Lexer.STRICT;
2181module.exports = Lexer;

/Users/army/Sites/homunculus/src/lexer/Token.js

100%
41
41
0
LineHitsSource
11var Class = require('../util/Class');
21var character = require('../util/character');
31var tid = 0;
41var types;
51var Token = Class(function(type, content, val, sIndex) {
6456402 this.t = type; //token类型
7456402 this.c = content; //token的字面内容,string包括头尾的引号
8456402 if(character.isNumber(val)) {
9494 sIndex = val;
10494 val = content;
11 }
12455908 else if(character.isUndefined(val)) {
131590 val = content;
141590 sIndex = -1;
15 }
16456402 this.v = val; //token的值,一般情况下等于content,特殊如string情况下值是不加头尾的引号
17456402 this.id = tid++; //token的索引
18456402 this.si = sIndex; //token在源码字符串中的索引
19}).methods({
20 type: function(t) {
211637792 if(!character.isUndefined(t)) {
2221502 this.t = t;
23 }
241637792 return this.t;
25 },
26 content: function(c) {
271704074 if(!character.isUndefined(c)) {
281 this.c = c;
29 }
301704074 return this.c;
31 },
32 val: function(v) {
33454320 if(!character.isUndefined(v)) {
341 this.v = v;
35 }
36454320 return this.v;
37 },
38 tag: function(t) {
398 if(!character.isUndefined(t)) {
401 this.t = t;
41 }
428 return Token.type(this.t);
43 },
44 tid: function(id) {
452 if(!character.isUndefined(id)) {
461 this.id = id;
47 }
482 return this.id;
49 },
50 sIndex: function(si) {
516 if(!character.isUndefined(si)) {
521 this.si = si;
53 }
546 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) {
7913 if(character.isUndefined(types)) {
801 types = [];
811 Object.keys(Token).forEach(function(o) {
8225 if(typeof Token[o] == 'number') {
8321 types[Token[o]] = o;
84 }
85 });
86 }
8713 return types[tag];
88 }
89});
901module.exports = Token;

/Users/army/Sites/homunculus/src/lexer/match/CharacterSet.js

100%
10
10
0
LineHitsSource
11var Match = require('./Match');
21var character = require('../../util/character');
31var CharacterSet = Match.extend(function(type, str, setPReg) {
4544 Match.call(this, type, setPReg);
5544 this.str = str;
6}).methods({
7 match: function(c, code, index) {
8141104 var isIn = this.str.indexOf(c) > -1;
9141104 if(isIn) {
10133573 this.result = c;
11 }
12141104 return isIn;
13 }
14});
151module.exports = CharacterSet;

/Users/army/Sites/homunculus/src/lexer/match/CompleteEqual.js

100%
7
7
0
LineHitsSource
11var Match = require('./Match');
21var character = require('../../util/character');
31var CompleteEqual = Match.extend(function(type, result, setPReg) {
410192 Match.call(this, type, setPReg);
510192 this.result = result;
6}).methods({
7 match: function(c, code, index) {
86038931 return code.substr(--index, this.result.length) == this.result;
9 }
10});
111module.exports = CompleteEqual;

/Users/army/Sites/homunculus/src/lexer/match/LineParse.js

96%
32
31
1
LineHitsSource
11var Match = require('./Match');
21var Token = require('../Token');
31var character = require('../../util/character');
41var LineParse = Match.extend(function(type, begin, end, mutiline, setPReg) {
5820 if(character.isUndefined(mutiline)) {
60 mutiline = false;
7 }
8820 Match.call(this, type, setPReg);
9820 this.begin = begin;
10820 this.end = end;
11820 this.msg = null;
12820 this.mutiline = mutiline;
13 }).methods({
14 match: function(c, code, index) {
15763466 this.msg = null;
16763466 if(this.begin == code.charAt(index - 1)) {
176829 var len = code.length,
18 lastIndex = index - 1,
19 res = false;
206829 while(index < len) {
2157362 var c = code.charAt(index++);
22 //转义
2357362 if(c == character.BACK_SLASH) {
24402 if(code.charAt(index++) == character.ENTER) {
251 index++;
26 }
27 }
2856960 else if(c == character.LINE && !this.mutiline) {
291 break;
30 }
3156959 else if(c == this.end) {
326827 res = true;
336827 break;
34 }
35 }
366829 if(!res) {
372 this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase() + ' literal';
38 }
396829 this.result = code.slice(lastIndex, index);
406829 return true;
41 }
42756637 return false;
43 },
44 error: function() {
456829 return this.msg;
46 },
47 val: function() {
486829 return this.content().slice(this.begin.length, -this.end.length);
49 }
50 });
511module.exports = LineParse;

/Users/army/Sites/homunculus/src/lexer/match/LineSearch.js

100%
33
33
0
LineHitsSource
11var Match = require('./Match');
21var Token = require('../Token');
31var character = require('../../util/character');
41var LineSearch = Match.extend(function(type, begin, end, contain, setPReg) {
5548 if(character.isUndefined(contain)) {
6274 contain = false;
7 }
8548 Match.call(this, type, setPReg);
9548 this.begin = begin;
10548 this.end = end;
11548 this.contain = contain;
12548 this.msg = null;
13}).methods({
14 match: function(c, code, index) {
15522214 this.msg = null;
16522214 if(this.begin == code.substr(--index, this.begin.length)) {
17 //支持多个end匹配时不支持包含选项
185001 if(!this.contain && Array.isArray(this.end)) {
194786 for(var j = 0, len = this.end.length; j < len; j++) {
2014358 var i = code.indexOf(this.end[j], index + this.begin.length);
2114358 if(i != -1) {
224785 this.result = code.slice(index, i);
234785 return true;
24 }
25 }
26 //都不匹配时到末尾
271 this.result = code.slice(index);
281 return true;
29 }
30 else {
31215 var i = code.indexOf(this.end, index + this.begin.length);
32215 if(i == -1) {
331 if(this.contain) {
341 this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase();
35 }
361 i = code.length;
37 }
38214 else if(this.contain) {
39214 i += this.end.length;
40 }
41215 this.result = code.slice(index, i);
42215 return true;
43 }
44 }
45517213 return false;
46 },
47 error: function() {
485001 return this.msg;
49 }
50});
511module.exports = LineSearch;

/Users/army/Sites/homunculus/src/lexer/match/Match.js

91%
24
22
2
LineHitsSource
11var Class = require('../../util/Class');
21var character = require('../../util/character');
31var Lexer = require('../Lexer');
41module.exports = Class(function(type, setPReg, special, parenthese) {
513220 this.type = type;
613220 if(character.isUndefined(setPReg)) {
72884 setPReg = Lexer.IGNORE;
8 }
913220 this.setPReg = setPReg;
1013220 this.result = null;
11 //忽略0,是1,否2,特殊3
1213220 if(setPReg) {
1310336 if(character.isUndefined(special)) {
1410064 special = function() {
150 return Lexer.IGNORE;
16 };
17 }
1810336 if(character.isUndefined(parenthese)) {
1910064 parenthese = function() {
20122626 return false;
21 };
22 }
23 }
2413220 this.special = special;
2513220 this.parenthese = parenthese;
26}).methods({
27 tokenType: function() {
28454318 return this.type;
29 },
30 perlReg: function() {
31875202 return this.setPReg;
32 },
33 val: function() {
34447489 return this.content();
35 },
36 content: function() {
371652118 return this.result;
38 },
39 match: function(c, code, index) {
40 //需被实现
410 throw new Error('match needs to be implement');
42 },
43 error: function() {
44338849 return false;
45 }
46});

/Users/army/Sites/homunculus/src/lexer/match/RegMatch.js

100%
23
23
0
LineHitsSource
11var Match = require('./Match');
21var RegMatch = Match.extend(function(type, reg, valid, setPReg, special, parenthese) {
31116 if(typeof valid == 'number') {
4272 parenthese = special;
5272 special = setPReg;
6272 setPReg = valid;
7272 valid = null;
8 }
91116 Match.call(this, type, setPReg, special, parenthese);
101116 this.reg = reg;
111116 this.valid = valid;
12 }).methods({
13 match: function(c, code, index) {
14422287 var self = this,
15 res = self.reg.exec(code.slice(index - 1));
16422287 self.msg = null;
17422287 if(res) {
18103639 self.result = res[0];
19103639 if(self.valid) {
207537 for(var i = 0, keys = Object.keys(self.valid), len = keys.length; i < len; i++) {
217537 if(self.valid[keys[i]].test(self.result)) {
222 self.msg = keys[i];
232 break;
24 }
25 }
26 }
27103639 return true;
28 }
29318648 return false;
30 },
31 error: function() {
32103639 return this.msg;
33 }
34 });
351module.exports = RegMatch;

/Users/army/Sites/homunculus/src/lexer/rule/EcmascriptRule.js

100%
36
36
0
LineHitsSource
11var Rule = require('./Rule');
21var LineSearch = require('../match/LineSearch');
31var LineParse = require('../match/LineParse');
41var CompleteEqual = require('../match/CompleteEqual');
51var RegMatch = require('../match/RegMatch');
61var CharacterSet = require('../match/CharacterSet');
71var Token = require('../Token');
81var Lexer = require('../Lexer');
91var character = require('../../util/character');
101var EcmascriptRule = Rule.extend(function() {
11272 var self = this;
12272 Rule.call(self, EcmascriptRule.KEYWORDS, true);
13
14272 self.addMatch(new CompleteEqual(Token.BLANK, character.BLANK));
15272 self.addMatch(new CompleteEqual(Token.TAB, character.TAB));
16272 self.addMatch(new CompleteEqual(Token.LINE, character.ENTER + character.LINE));
17272 self.addMatch(new CompleteEqual(Token.LINE, character.ENTER));
18272 self.addMatch(new CompleteEqual(Token.LINE, character.LINE));
19
20272 self.addMatch(new LineSearch(Token.COMMENT, '//', [character.ENTER + character.LINE, character.ENTER, character.LINE]));
21272 self.addMatch(new LineSearch(Token.COMMENT, '/*', '*/', true));
22272 self.addMatch(new LineParse(Token.STRING, '"', '"', false, Lexer.IS_REG));
23272 self.addMatch(new LineParse(Token.STRING, "'", "'", false, Lexer.IS_REG));
24272 self.addMatch(new LineParse(Token.TEMPLATE, '`', '`', true, Lexer.IS_REG));
25
26272 self.addMatch(new RegMatch(Token.ID, /^[$a-zA-Z_][$\w]*/, Lexer.SPECIAL, function() {
2796102 return !!(self.keyWords().hasOwnProperty(this.content()));
28 }, function() {
2996102 return ['if', 'for', 'while'].indexOf(this.content()) != -1;
30 }));
31
32272 self.addMatch(new RegMatch(Token.NUMBER, /^\.\d+(?:E[+-]?\d*)?/i, {
33 'SyntaxError: missing exponent': /E[+-]?$/i
34 }, Lexer.NOT_REG));
35
36272 self.addMatch(new CompleteEqual(Token.SIGN, ']', Lexer.NOT_REG));
37
38272 ['*=', '/=', '+=', '-=', '%=', '^=', '&=', '|=', '&&', '--', '++', '===', '==', '!==', '!=', '||', '>>>=', '<<<=', '<<<', '>>>', '>>=', '<<=', '<<', '>>', '>=', '<=', '...', '?:', '=>'].forEach(function(o) {
397888 self.addMatch(new CompleteEqual(Token.SIGN, o, Lexer.IS_REG));
40 });
41272 self.addMatch(new CharacterSet(Token.SIGN, ':;/?.,[]{}~!^|%=-+*()~><&\\', Lexer.IS_REG));
42
43272 self.addMatch(new RegMatch(Token.NUMBER, /^0x[\da-f]*/i, {
44 "SyntaxError: missing hexadecimal digits after '0x'": /^0x$/i
45 }, Lexer.NOT_REG));
46272 self.addMatch(new RegMatch(Token.NUMBER, /^\d+\.?\d*(?:E[+-]?\d*)?/i, {
47 'SyntaxError: missing exponent': /E[+-]?$/i
48 }, Lexer.NOT_REG));
49
50272 self.addMatch(new CompleteEqual(Token.LINE, '\u2028'));
51272 self.addMatch(new CompleteEqual(Token.LINE, '\u2029'));
52272 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});
561module.exports = EcmascriptRule;

/Users/army/Sites/homunculus/src/lexer/rule/Rule.js

100%
14
14
0
LineHitsSource
11var Class = require('../../util/Class');
21var Rule = Class(function(words, pReg) {
3274 var self = this;
4274 self.kw = {};
5274 words.forEach(function(o) {
612882 self.kw[o] = true;
7 });
8274 self.pReg = pReg || false;
9274 self.matchList = [];
10 }).methods({
11 perlReg: function() {
12232 return this.pReg;
13 },
14 addMatch: function(match) {
1513220 this.matchList.push(match);
1613220 return this;
17 },
18 matches: function() {
19454319 return this.matchList;
20 },
21 keyWords: function() {
22192204 return this.kw;
23 }
24 });
251module.exports = Rule;

/Users/army/Sites/homunculus/src/parser/js/Context.js

99%
204
203
1
LineHitsSource
11var Class = require('../../util/Class');
21var JsNode = require('./Node');
31var Token = require('../../lexer/Token');
41var Parser = require('./Parser');
51var id = 0;
61var Context = Class(function(parent, name) {
765 this.id = id++;
865 this.parser = new Parser();
965 this.parent = parent || null; //父上下文,如果是全局则为空
1065 this.name = name || null; //上下文名称,即函数名,函数表达式为空,全局也为空
1165 this.children = []; //函数声明或函数表达式所产生的上下文
1265 this.childrenMap = Object.create(null); //键是函数名,值是上下文,匿名函数表达式键为cid
1365 this.vars = []; //变量var声明
1465 this.varsMap = Object.create(null); //键为id字面量,值是它的token的节点
1565 this.vardeclMap = Object.create(null); //var赋值记录,优先级vardecl > fndecl > varnodecl
1665 this.params = []; //形参,函数上下文才有,即全局无
1765 this.paramsMap = Object.create(null); //键为id字面量,值是它的token的节点
1865 this.aParams = []; //实参,函数表达式才有
1965 this.vids = []; //上下文环境里用到的变量id
2065 this.vidsMap = Object.create(null); //键为id字面量,值是它的token的节点
2165 this.returns = []; //上下文环境里return语句
2265 this.node = null; //对应的ast的节点
2365 this.thisIs = null; //this指向,仅函数表达式call或apply执行时有用
2465 if(!this.isTop()) {
2526 this.parent.addChild(this);
26 }
27}).methods({
28 parse: function(code) {
2935 var ast;
3035 if(code instanceof JsNode) {
311 ast = code;
32 }
33 else {
3434 ast = this.parser.parse(code);
35 }
3635 recursion(ast, this);
3735 return this;
38 },
39 getId: function() {
4029 return this.id;
41 },
42 getName: function() {
4327 return this.name;
44 },
45 getParent: function() {
462 return this.parent;
47 },
48 isTop: function() {
4966 return !this.parent;
50 },
51 isFnexpr: function() {
521 return !this.isTop() && !this.name;
53 },
54 hasParam: function(p) {
556 return p in this.paramsMap;
56 },
57 getParams: function() {
581 return this.params;
59 },
60 addParam: function(p) {
61 //形参不可能重复,无需判断
6210 this.paramsMap[p] = this.params.length;
6310 this.params.push(p);
6410 return this;
65 },
66 getAParams: function() {
674 return this.aParams;
68 },
69 addAParam: function(ap) {
709 this.aParams.push(ap);
719 return this;
72 },
73 getChild: function(name) {
747 return this.childrenMap[name];
75 },
76 getChildren: function() {
7715 return this.children;
78 },
79 //通过name查找函数声明,id查找表达式
80 hasChild: function(name) {
8148 return name in this.childrenMap;
82 },
83 addChild: function(child) {
8426 var name = child.getName();
85 //函数表达式名字为空用id删除
8626 if(name) {
8712 if(name in this.vardeclMap) {
881 return this;
89 }
9011 this.delVar(name);
9111 this.delChild(name);
92 }
93 else {
9414 this.delChild(child.getId());
95 }
9625 name = name || child.getId();
9725 this.childrenMap[name] = child;
9825 this.children.push(child);
9925 return this;
100 },
101 //name函数声明,id表达式
102 delChild: function(name) {
10338 if(this.hasChild(name)) {
1041 var i = this.children.indexOf(this.childrenMap[name]);
1051 this.children.splice(i, 1);
1061 delete this.childrenMap[name];
107 }
10838 return this;
109 },
110 hasVar: function(v) {
11138 return v in this.varsMap;
112 },
113 addVar: function(node, assign) {
11417 var v = node.leaves()[0].token().content();
115 //赋值拥有最高优先级,会覆盖掉之前的函数声明和var
11617 if(assign) {
11713 this.delVar(v);
11813 this.delChild(v);
11913 this.vardeclMap[v] = true;
120 }
121 //仅仅是var声明无赋值,且已有过声明或函数,忽略之
1224 else if(this.hasVar(v) || this.hasChild(v)) {
1232 return this;
124 }
12515 this.varsMap[v] = node;
12615 this.vars.push(node);
12715 return this;
128 },
129 delVar: function(v) {
13024 if(this.hasVar(v)) {
1312 var i = this.vars.indexOf(this.varsMap[v]);
1322 this.vars.splice(i, 1);
1332 delete this.varsMap[v];
134 }
13524 return this;
136 },
137 getVars: function() {
1384 return this.vars;
139 },
140 addReturn: function(node) {
1415 this.returns.push(node);
1425 return this;
143 },
144 getReturns: function() {
1454 return this.returns;
146 },
147 hasVid: function(v) {
1488 return v in this.vidsMap;
149 },
150 getVid: function(v) {
1511 return this.vidsMap[v];
152 },
153 addVid: function(node) {
15418 var v = node.token().content();
15518 this.vids.push(node);
15618 this.vidsMap[v] = this.vidsMap[v] || [];
15718 this.vidsMap[v].push(node);
15818 return this;
159 },
160 getVids: function() {
1612 return this.vids;
162 },
163 getNode: function() {
1641 return this.node;
165 },
166 setNode: function(n) {
16726 this.node = n;
16826 return this;
169 },
170 setThis: function(t) {
1714 this.thisIs = t;
1724 return this;
173 },
174 getThis: function() {
1753 return this.thisIs;
176 }
177});
178
1791function recursion(node, context) {
180668 var isToken = node.name() == JsNode.TOKEN;
181668 var isVirtual = isToken && node.token().type() == Token.VIRTUAL;
182668 if(isToken) {
183385 if(!isVirtual) {
184379 var token = node.token();
185379 var s = token.content();
186379 if(s == 'return') {
1875 context.addReturn(node);
188 }
189 }
190 }
191 else {
192283 if(node.name() == JsNode.VARDECL) {
19317 vardecl(node, context);
194 }
195266 else if(node.name() == JsNode.FNDECL) {
19612 context = fndecl(node, context);
197 }
198254 else if(node.name() == JsNode.FNEXPR) {
19914 context = fnexpr(node, context);
200 }
201240 else if(node.name() == JsNode.PRMREXPR) {
20252 prmrexpr(node, context);
203 }
204283 node.leaves().forEach(function(leaf, i) {
205633 recursion(leaf, context);
206 });
207 }
208}
2091function vardecl(node, context) {
21017 var leaves = node.leaves();
21117 var assign = !!leaves[1];
21217 context.addVar(node, assign);
213}
2141function fndecl(node, context) {
21512 var v = node.leaves()[1].leaves().content();
21612 var child = new Context(context, v);
21712 child.setNode(node);
21812 var params = node.leaves()[3];
21912 if(params.name() == JsNode.FNPARAMS) {
2201 addParam(params, child);
221 }
22212 return child;
223}
2241function fnexpr(node, context) {
225 //函数表达式name为空
22614 var child = new Context(context);
22714 child.setNode(node);
228 //记录形参
22914 var params;
23014 var v = node.leaves()[1];
23114 if(v.token().content() != '(') {
2326 params = node.leaves()[3];
233 }
234 else {
2358 params = node.leaves()[2];
236 }
23714 if(params.name() == JsNode.FNPARAMS) {
2387 addParam(params, child);
239 }
240 //匿名函数检查实参传入情况,包括call和apply设置this
24114 var next = node.next();
242 //!function(){}()形式
24314 if(next && next.name() == JsNode.ARGS) {
2447 var leaves = next.leaves();
245 //长度2为()空参数,长度3有参数,第2个节点
2467 if(leaves.length == 3) {
2471 addAParam(leaves[1], child);
248 }
249 }
250 //call或applay
2517 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) {
2572 var mmb = node.parent();
2582 if(mmb.name() == JsNode.MMBEXPR) {
2592 var callexpr = mmb.parent();
2602 if(callexpr.name() == JsNode.CALLEXPR) {
2612 var isApply = next.next().token().content() == 'apply';
2622 next = mmb.next();
2632 if(next && next.name() == JsNode.ARGS) {
2642 var leaves = next.leaves();
265 //长度2为()空参数,长度3有参数,第2个节点
2662 if(leaves.length == 3) {
2672 isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child);
268 }
269 }
270 }
271 }
272 }
273 //(function(){})()形式
274 else {
2755 var prmr = node.parent();
2765 var prev = node.prev();
2775 if(prmr.name() == JsNode.PRMREXPR
278 && prev
279 && prev.name() == JsNode.TOKEN
280 && prev.token().content() == '(') {
2815 next = prmr.next();
2825 if(next && next.name() == JsNode.ARGS) {
2833 var leaves = next.leaves();
284 //长度2为()空参数,长度3有参数,第2个节点
2853 if(leaves.length == 3) {
2861 addAParam(leaves[1], child);
287 }
288 }
289 //(function(){}).call()形式
2902 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) {
2962 var mmb = prmr.parent();
2972 if(mmb.name() == JsNode.MMBEXPR) {
2982 var callexpr = mmb.parent();
2992 if(callexpr.name() == JsNode.CALLEXPR) {
3002 var isApply = next.next().token().content() == 'apply';
3012 next = mmb.next();
3022 if(next && next.name() == JsNode.ARGS) {
3032 var leaves = next.leaves();
304 //长度2为()空参数,长度3有参数,第2个节点
3052 if(leaves.length == 3) {
3062 isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child);
307 }
308 else {
3090 child.setThis(undefined);
310 }
311 }
312 }
313 }
314 }
315 }
316 }
31714 return child;
318}
319//支持es6
3201function addParam(params, child) {
3218 params.leaves().forEach(function(leaf, i) {
32213 if(leaf.name() == JsNode.TOKEN && leaf.token().content() != ',') {
3239 child.addParam(leaf.token().content());
324 }
3254 else if(leaf.name() == JsNode.RESTPARAM) {
3261 child.addParam(leaf.leaves()[1].token().content());
327 }
328 });
329}
3301function addAParam(params, child) {
3312 params.leaves().forEach(function(leaf, i) {
3326 if(i % 2 == 0) {
3334 child.addAParam(leaf);
334 }
335 });
336}
3371function addCallAParam(params, child) {
3382 params.leaves().forEach(function(leaf, i) {
3396 if(i == 0) {
3402 child.setThis(leaf);
341 }
3424 else if(i % 2 == 1) {
3432 child.addAParam(leaf);
344 }
345 });
346}
3471function addApplyAParam(params, child) {
3482 child.setThis(params.leaves()[0]);
3492 if(params.leaves()[2]) {
3502 params.leaves()[2].leaves()[0].leaves().forEach(function(leaf, i) {
3518 if(i % 2 == 1) {
3523 child.addAParam(leaf);
353 }
354 });
355 }
356}
3571function prmrexpr(node, context) {
35852 var first = node.leaves()[0];
35952 if(first.name() == JsNode.TOKEN) {
36048 var token = first.token();
36148 if(token.type() == Token.ID || token.content() == 'this') {
36218 context.addVid(first);
363 }
364 }
365}
366
3671module.exports = Context;

/Users/army/Sites/homunculus/src/parser/js/Node.js

97%
47
46
1
LineHitsSource
11var Class = require('../../util/Class');
21var Node = Class(function(type, children) {
3770922 this.type = type;
4770922 if(type == Node.TOKEN) {
5140302 this.children = children;
6 }
7630620 else if(Array.isArray(children)) {
80 this.children = children;
9 }
10 else {
11630620 this.children = children ? [children] : [];
12 }
13770922 this.p = null;
14770922 this.pr = null;
15770922 this.ne = null;
16770922 return this;
17}).methods({
18 name: function() {
19240875 return this.type;
20 },
21 leaves: function() {
2299014 return this.children;
23 },
24 leaf: function(i) {
251 return this.children[i];
26 },
27 number: function() {
281 return this.children.length;
29 },
30 add: function() {
31169852 var self = this;
32169852 var args = Array.prototype.slice.call(arguments, 0);
33169852 args.forEach(function(node) {
34238950 node.parent(self);
35238950 var last = self.children[self.children.length - 1];
36238950 if(last) {
37140129 last.next(node);
38140129 node.prev(last);
39 }
40238950 self.children.push(node);
41 });
42169852 return self;
43 },
44 token: function() {
45278901 return this.children;
46 },
47 parent: function(p) {
48238966 if(p) {
49238950 this.p = p;
50 }
51238966 return this.p;
52 },
53 prev: function(pr) {
54140136 if(pr) {
55140129 this.pr = pr;
56 }
57140136 return this.pr;
58 },
59 next: function(ne) {
60140169 if(ne) {
61140129 this.ne = ne;
62 }
63140169 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) {
1452 if(!s) {
1461 throw new Error('empty value');
147 }
1481 if(!keys) {
1491 var self = this;
1501 keys = {};
1511 Object.keys(this).forEach(function(k) {
15281 var v = self[k];
15381 keys[v] = k;
154 });
155 }
1561 return keys[s];
157 }
158});
1591var keys;
1601module.exports = Node;

/Users/army/Sites/homunculus/src/parser/js/Parser.js

97%
727
710
17
LineHitsSource
11var Class = require('../../util/Class');
21var character = require('../../util/character');
31var Lexer = require('../../lexer/Lexer');
41var Rule = require('../../lexer/rule/EcmascriptRule');
51var Token = require('../../lexer/Token');
61var Node = require('./Node');
71var S = {};
81S[Token.BLANK] = S[Token.TAB] = S[Token.COMMENT] = S[Token.LINE] = S[Token.ENTER] = true;
91var Parser = Class(function(lexer) {
10231 this.init(lexer);
11}).methods({
12 parse: function(code) {
13196 this.lexer.parse(code);
14196 this.tree = this.program();
15162 return this.tree;
16 },
17 ast: function() {
184 return this.tree;
19 },
20 init: function(lexer) {
21232 this.look = null;
22232 this.tokens = null;
23232 this.lastLine = 1;
24232 this.lastCol = 1;
25232 this.line = 1;
26232 this.col = 1;
27232 this.index = 0;
28232 this.length = 0;
29232 this.ignores = {};
30232 this.hasMoveLine = false;
31232 this.tree = {};
32232 if(lexer) {
33166 this.lexer = lexer;
34 }
3566 else if(this.lexer) {
361 this.lexer.init();
37 }
38 else {
3965 this.lexer = new Lexer(new Rule());
40 }
41 },
42 program: function() {
43196 this.tokens = this.lexer.tokens();
44196 this.length = this.tokens.length;
45196 if(this.tokens.length) {
46194 this.move();
47 }
48196 var node = new Node(Node.PROGRAM);
49196 while(this.look) {
50228 node.add(this.element());
51 }
52162 return node;
53 },
54 element: function(allowSuper) {
555396 if(this.look.content() == 'function') {
56201 return this.fndecl();
57 }
585195 else if(this.look.content() == 'class') {
5913 return this.classdecl();
60 }
61 else {
625182 return this.stmt(allowSuper);
63 }
64 },
65 stmt: function(allowSuper) {
6610868 if(!this.look) {
671 this.error();
68 }
6910867 switch(this.look.content()) {
70 case 'let':
713 return this.letstmt();
72 case 'const':
733 return this.cststmt();
74 case 'var':
751169 return this.varstmt();
76 case '{':
771791 return this.block();
78 case ';':
7935 return this.emptstmt();
80 case 'if':
811692 return this.ifstmt();
82 case 'do':
83 case 'while':
84 case 'for':
85458 return this.iterstmt();
86 case 'continue':
8718 return this.cntnstmt();
88 case 'break':
89123 return this.brkstmt();
90 case 'return':
911636 return this.retstmt();
92 case 'with':
931 return this.withstmt();
94 case 'switch':
958 return this.swchstmt();
96 case 'throw':
9739 return this.thrstmt();
98 case 'try':
9955 return this.trystmt();
100 case 'debugger':
1011 return this.debstmt();
102 case 'super':
1033 if(!allowSuper) {
1041 this.error('super must in a class');
105 }
1062 return this.superstmt();
107 case 'import':
1080 return this.imptstmt();
109 default:
1103832 if(this.look.type() == Token.ID) {
1112944 for(var i = this.index; i < this.length; i++) {
1123956 var token = this.tokens[i];
1133956 if(!S[token.type()]) {
1142941 if(token.content() == ':') {
1152 return this.labstmt();
116 }
117 else {
1182939 return this.exprstmt();
119 }
120 }
121 }
122 }
123891 return this.exprstmt();
124 }
125 },
126 exprstmt: function() {
1273830 var node = new Node(Node.EXPRSTMT);
1283830 node.add(this.expr(), this.match(';'));
1293821 return node;
130 },
131 cststmt: function(noSem) {
1323 var node = new Node(Node.CSTSTMT);
1333 node.add(
134 this.match('const'),
135 this.vardecl()
136 );
1373 while(this.look && this.look.content() == ',') {
1381 node.add(
139 this.match(),
140 this.vardecl()
141 );
142 }
1433 if(!noSem) {
1443 node.add(this.match(';'));
145 }
1463 return node;
147 },
148 letstmt: function(noSem) {
1493 var node = new Node(Node.LETSTMT);
1503 node.add(
151 this.match('let'),
152 this.vardecl()
153 );
1543 while(this.look && this.look.content() == ',') {
1551 node.add(
156 this.match(),
157 this.vardecl()
158 );
159 }
1603 if(!noSem) {
1613 node.add(this.match(';'));
162 }
1633 return node;
164 },
165 varstmt: function(noSem) {
1661253 var node = new Node(Node.VARSTMT);
1671253 node.add(
168 this.match('var'),
169 this.vardecl()
170 );
1711246 while(this.look && this.look.content() == ',') {
1721555 node.add(
173 this.match(),
174 this.vardecl()
175 );
176 }
1771246 if(!noSem) {
1781162 node.add(this.match(';'));
179 }
1801246 return node;
181 },
182 vardecl: function() {
1832816 var node = new Node(Node.VARDECL);
1842816 if(!this.look) {
1851 this.error('missing variable name');
186 }
1872815 if(['[', '{'].indexOf(this.look.content()) > -1) {
18810 node.add(this.bindpat());
1896 if(!this.look || this.look.content() != '=') {
1901 this.error('missing = in destructuring declaration');
191 }
1925 node.add(this.assign());
193 }
194 else {
1952805 node.add(this.match(Token.ID, 'missing variable name'));
1962805 if(this.look && this.look.content() == '=') {
1971919 node.add(this.assign());
198 }
199 }
2002809 return node;
201 },
202 bindpat: function() {
20315 if(this.look.content() == '[') {
2048 return this.arrbindpat();
205 }
2067 else if(this.look.content() == '{') {
2077 return this.objbindpat();
208 }
209 },
210 arrbindpat: function() {
2118 var node = new Node(Node.ARRBINDPAT);
2128 node.add(this.match('['));
2138 while(this.look && this.look.content() != ']') {
21418 if(this.look.content() == ',') {
2155 node.add(this.match());
216 }
21713 else if(this.look.content() == '...') {
2182 break;
219 }
220 else {
22111 node.add(this.bindelem());
222 }
223 }
2248 if(this.look.content() == '...') {
2252 node.add(this.restparam());
226 }
2278 node.add(this.match(']', 'missing ] after element list'));
2288 return node;
229 },
230 bindelem: function() {
23113 var node = new Node(Node.BINDELEM);
23213 if(['[', '{'].indexOf(this.look.content()) > -1) {
2335 node.add(this.bindpat());
2345 if(this.look && this.look.content() == '=') {
2351 node.add(this.assign());
236 }
237 }
238 else {
2398 return this.singlename();
240 }
2415 return node;
242 },
243 singlename: function() {
24412 var node = new Node(Node.SINGLENAME);
24512 node.add(this.match(Token.ID));
24612 if(this.look && this.look.content() == '=') {
2474 node.add(this.assign());
248 }
24912 return node;
250 },
251 objbindpat: function() {
2527 var node = new Node(Node.OBJBINDPAT);
2537 node.add(this.match('{'));
2547 while(this.look && this.look.content() != '}') {
2558 node.add(this.bindpropt());
2566 if(this.look && this.look.content() == ',') {
2572 node.add(this.match());
258 }
259 }
2605 node.add(this.match('}', 'missing } after property list'));
2613 return node;
262 },
263 bindpropt: function() {
2648 var node = new Node(Node.BINDPROPT);
2658 switch(this.look.type()) {
266 case Token.ID:
267 case Token.STRING:
268 case Token.NUMBER:
2697 break;
270 default:
2711 this.error('invalid property id');
272 }
273 //根据LL2分辨是PropertyName[?Yield, ?GeneratorParameter] : BindingElement[?Yield, ?GeneratorParameter]
274 //还是SingleNameBinding [?Yield, ?GeneratorParameter]
2757 for(var i = this.index; i < this.length; i++) {
2766 var next = this.tokens[i];
2776 if(!S[next.tag()]) {
2786 if(next.content() == ':') {
2792 node.add(this.match(), this.match());
2802 node.add(this.bindelem());
281 }
282 else {
2834 node.add(this.singlename());
284 }
2856 return node;
286 }
287 }
2881 this.error('missing : after property id');
289 },
290 assign: function() {
2911929 var node = new Node(Node.ASSIGN);
2921929 node.add(this.match('='));
2931929 if(!this.look) {
2941 this.error();
295 }
2961928 node.add(this.assignexpr());
2971928 return node;
298 },
299 block: function() {
3001906 var node = new Node(Node.BLOCK);
3011906 node.add(this.match('{'));
3021906 while(this.look && this.look.content() != '}') {
3032988 node.add(this.stmt());
304 }
3051906 node.add(this.match('}', 'missing } in compound statement'));
3061906 return node;
307 },
308 emptstmt: function() {
30935 var node = new Node(Node.EMPTSTMT);
31035 node.add(this.match(';'));
31135 return node;
312 },
313 ifstmt: function() {
3141692 var node = new Node(Node.IFSTMT);
3151692 node.add(
316 this.match('if'),
317 this.match('('),
318 this.expr(),
319 this.match(')'),
320 this.stmt()
321 );
3221691 if(this.look && this.look.content() == 'else') {
323325 node.add(
324 this.match('else'),
325 this.stmt()
326 );
327 }
3281691 return node;
329 },
330 iterstmt: function() {
331458 var node = new Node(Node.ITERSTMT);
332458 switch(this.look.content()) {
333 case 'do':
3349 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 );
3439 break;
344 case 'while':
345152 node.add(
346 this.match(),
347 this.match('('),
348 this.expr(),
349 this.match(')'),
350 this.stmt()
351 );
352152 break;
353 case 'for':
354297 node.add(
355 this.match(),
356 this.match('(')
357 );
358297 if(!this.look) {
3591 this.error();
360 }
361296 if(this.look.content() == 'var' || this.look.content() == 'let') {
36284 var node2 = this.look.content() == 'var' ? this.varstmt(true) : this.letstmt(true);
36384 if(!this.look) {
3641 this.error('missing ; after for-loop initializer');
365 }
36683 if(this.look.content() == 'in') {
36719 if(node2.leaves().length > 2) {
3681 this.error('invalid for/in left-hand side');
369 }
37018 node.add(node2);
37118 node.add(
372 this.match(),
373 this.expr()
374 );
375 }
376 else {
37764 node.add(node2);
37864 node.add(this.match(';'));
37964 if(this.look.content() != ';') {
38063 node.add(this.expr());
381 }
38263 node.add(this.match(';'));
38363 if(!this.look) {
3841 this.error();
385 }
38662 if(this.look.content() != ')') {
38759 node.add(this.expr());
388 }
389 }
390 }
391 else {
392212 if(this.look.content() == 'in') {
3931 this.error();
394 }
395211 var hasIn = false;
396211 for(var i = this.index; i < this.length; i++) {
3971924 var t = this.tokens[i];
3981924 if(t.content() == 'in') {
39987 hasIn = true;
40087 break;
401 }
4021837 else if(t.content() == ')') {
403120 break;
404 }
405 }
406211 if(hasIn) {
40787 node.add(this.expr(true), this.match('in'), this.expr());
408 }
409 else {
410124 if(this.look.content() != ';') {
41168 node.add(this.expr());
412 }
413 //for的;不能省略,强制判断
414124 if(!this.look || this.look.content() != ';') {
4151 this.error('missing ;')
416 }
417123 node.add(this.match(';'));
418123 if(!this.look) {
4191 this.error();
420 }
421122 if(this.look.content() != ';') {
422121 node.add(this.expr());
423 }
424122 if(!this.look || this.look.content() != ';') {
4251 this.error('missing ;')
426 }
427121 node.add(this.match(';'));
428121 if(!this.look) {
4291 this.error();
430 }
431120 if(this.look.content() != ')') {
432112 node.add(this.expr());
433 }
434 }
435 }
436287 node.add(this.match(')'));
437287 node.add(this.stmt());
438 }
439448 return node;
440 },
441 cntnstmt: function() {
44218 var node = new Node(Node.CNTNSTMT);
44318 node.add(this.match('continue', true));
44418 if(this.look && this.look.type() == Token.ID) {
4451 node.add(this.match());
446 }
44718 node.add(this.match(';'));
44818 return node;
449 },
450 brkstmt: function() {
451123 var node = new Node(Node.BRKSTMT);
452123 node.add(this.match('break', true));
453123 if(this.look && this.look.type() == Token.ID) {
4541 node.add(this.match());
455 }
456123 node.add(this.match(';'));
457123 return node;
458 },
459 retstmt: function() {
4601636 var node = new Node(Node.RETSTMT);
4611636 node.add(this.match('return', true));
462 //return后换行视作省略;,包括多行注释的换行
4631636 if(this.look) {
4641635 if(this.look.content() == ';'
465 || this.look.content() == '}'
466 || this.look.type() == Token.LINE
467 || this.look.type() == Token.COMMENT) {
46882 node.add(this.match(';'));
469 }
470 else {
4711553 node.add(this.expr(), this.match(';'));
472 }
473 }
474 else {
4751 node.add(this.match(';'));
476 }
4771636 return node;
478 },
479 withstmt: function() {
4801 var node = new Node(Node.WITHSTMT);
4811 node.add(
482 this.match('with'),
483 this.match('('),
484 this.expr(),
485 this.match(')'),
486 this.stmt()
487 );
4881 return node;
489 },
490 swchstmt: function() {
4918 var node = new Node(Node.SWCHSTMT);
4928 node.add(
493 this.match('switch'),
494 this.match('('),
495 this.expr(),
496 this.match(')'),
497 this.caseblock()
498 );
4997 return node;
500 },
501 caseblock: function() {
5028 var node = new Node(Node.CASEBLOCK);
5038 node.add(this.match('{'));
5048 while(this.look && this.look.content() != '}') {
505102 if(this.look.content() == 'case') {
50699 node.add(this.caseclause());
507 }
5083 else if(this.look.content() == 'default') {
5092 node.add(this.dftclause());
510 }
511 else {
5121 this.error('invalid switch statement');
513 }
514 }
5157 node.add(this.match('}'));
5167 return node;
517 },
518 caseclause: function() {
51999 var node = new Node(Node.CASECLAUSE);
52099 node.add(
521 this.match('case'),
522 this.expr(),
523 this.match(':')
524 );
52599 while(this.look
526 && this.look.content() != 'case'
527 && this.look.content() != 'default'
528 && this.look.content() != '}') {
529228 node.add(this.stmt());
530 }
53199 return node;
532 },
533 dftclause: function() {
5342 var node = new Node(Node.DFTCLAUSE);
5352 node.add(
536 this.match('default'),
537 this.match(':')
538 );
5392 while(this.look && this.look.content() != '}') {
5402 node.add(this.stmt());
541 }
5422 return node;
543 },
544 labstmt: function() {
5452 var node = new Node(Node.LABSTMT);
5462 node.add(
547 this.match(Token.ID),
548 this.match(':'),
549 this.stmt()
550 );
5512 return node;
552 },
553 thrstmt: function() {
55439 var node = new Node(Node.THRSTMT);
55539 node.add(
556 this.match('throw', true),
557 this.expr(),
558 this.match(';')
559 );
56039 return node;
561 },
562 trystmt: function() {
56355 var node = new Node(Node.TRYSTMT);
56455 node.add(
565 this.match('try'),
566 this.block()
567 );
56855 if(this.look && this.look.content() == 'catch') {
56954 node.add(this.cach());
57054 if(this.look && this.look.content() == 'finally') {
5715 node.add(this.finl());
572 }
573 }
574 else {
5751 node.add(this.finl());
576 }
57755 return node;
578 },
579 debstmt: function() {
5801 var node = new Node(Node.DEBSTMT);
5811 node.add(this.match('debugger'), this.match(';'));
5821 return node;
583 },
584 cach: function() {
58554 var node = new Node(Node.CACH);
58654 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 );
59354 return node;
594 },
595 finl: function() {
5966 var node = new Node(Node.FINL);
5976 node.add(
598 this.match('finally'),
599 this.block()
600 );
6016 return node;
602 },
603 superstmt: function() {
6042 var node = new Node(Node.SUPERSTMT);
6052 node.add(this.match('super'));
6062 if(!this.look) {
6070 this.error();
608 }
6092 if(this.look.content() == '.') {
6101 while(this.look && this.look.content() == '.') {
6112 node.add(this.match());
6122 if(!this.look) {
6130 this.error();
614 }
6152 if(this.look.content() == 'super') {
6161 node.add(this.match());
617 }
618 else {
6191 break;
620 }
621 }
6221 if(this.look.content() != '(') {
6231 node.add(this.match(Token.ID));
6241 while(this.look && this.look.content() == '.') {
6250 node.add(this.match(), this.match(Token.ID));
626 }
627 }
628 }
6292 node.add(
630 this.args(),
631 this.match(';')
632 );
6332 return node;
634 },
635 imptstmt: function() {
6360 var node = new Node(Node.IMPTSTMT);
6370 return node;
638 },
639 fndecl: function() {
640201 var node = new Node(Node.FNDECL);
641201 node.add(
642 this.match('function'),
643 this.match(Token.ID, 'function statement requires a name'),
644 this.match('(')
645 );
646201 if(!this.look) {
6471 this.error('missing formal parameter');
648 }
649200 if(this.look.content() != ')') {
650157 node.add(this.fnparams());
651 }
652200 node.add(
653 this.match(')'),
654 this.match('{'),
655 this.fnbody(),
656 this.match('}')
657 );
658200 return node;
659 },
660 fnexpr: function() {
6611625 var node = new Node(Node.FNEXPR);
6621625 node.add(this.match('function'));
6631625 if(!this.look) {
6641 this.error('missing formal parameter');
665 }
6661624 if(this.look.type() == Token.ID) {
66722 node.add(this.match());
668 }
6691624 node.add(this.match('('));
6701624 if(!this.look) {
6711 this.error();
672 }
6731623 if(this.look.content() != ')') {
6741147 node.add(this.fnparams());
675 }
6761621 node.add(
677 this.match(')'),
678 this.match('{'),
679 this.fnbody(),
680 this.match('}', 'missing } in compound statement')
681 );
6821621 return node;
683 },
684 fnparams: function() {
6851305 var node = new Node(Node.FNPARAMS);
6861305 while(this.look && this.look.content() != ')' && this.look.content() != '...') {
6872286 node.add(this.match(Token.ID, 'missing formal parameter'));
6882285 if(this.look) {
6892285 if(this.look.content() == ',') {
690983 node.add(this.match());
691 }
6921302 else if(this.look.content() == '=') {
6934 node.add(this.bindelement());
6944 if(this.look && this.look.content() == ',') {
6953 node.add(this.match());
696 }
697 }
698 }
699 }
7001304 if(!this.look) {
7011 this.error('missing ) after formal parameters');
702 }
7031303 if(this.look.content() == '...') {
7044 node.add(this.restparam());
705 }
7061303 return node;
707 },
708 bindelement: function() {
7094 var node = new Node(Node.BINDELEMENT);
7104 node.add(this.match('='), this.assignexpr());
7114 return node;
712 },
713 restparam: function() {
7146 var node = new Node(Node.RESTPARAM);
7156 node.add(this.match('...'), this.match(Token.ID));
7166 return node;
717 },
718 fnbody: function(allowSuper) {
7191828 var node = new Node(Node.FNBODY);
7201828 while(this.look && this.look.content() != '}') {
7215168 node.add(this.element(allowSuper));
722 }
7231828 return node;
724 },
725 classdecl: function() {
72613 var node = new Node(Node.CLASSDECL);
72713 node.add(this.match('class'), this.match(Token.ID));
72813 if(!this.look) {
7291 this.error();
730 }
73112 if(this.look.content() == 'extends') {
7323 node.add(this.heratige());
733 }
73412 node.add(
735 this.match('{'),
736 this.classbody(),
737 this.match('}')
738 );
7399 return node;
740 },
741 heratige: function() {
7423 var node = new Node(Node.HERITAGE);
7433 node.add(this.match('extends'), this.match(Token.ID));
7443 return node;
745 },
746 classbody: function() {
74712 var node = new Node(Node.CLASSBODY),
748 methods = {},
749 hasStatic = false;
75012 while(this.look && this.look.content() != '}') {
75111 if(this.look.content() == ';') {
7521 node.add(this.match());
7531 continue;
754 }
75510 hasStatic = false;
75610 if(this.look.content() == 'static') {
7572 node.add(this.match());
7582 hasStatic = true;
759 }
76010 if(!this.look) {
7611 this.error();
762 }
7639 node.add(this.method(hasStatic, methods));
764 }
7659 return node;
766 },
767 method: function(hasStatic, methods, statics) {
7689 var node = new Node(Node.METHOD);
7699 if(this.look.content() == 'get') {
7701 node.add(this.match(), this.getfn());
771 }
7728 else if(this.look.content() == 'set') {
7731 node.add(this.match(), this.setfn());
774 }
775 else {
7767 node.add(this.match(Token.ID));
7777 var id = node.leaves()[0].token().content();
7787 if(methods.hasOwnProperty(id)) {
7791 this.error('duplicate method decl in class');
780 }
7816 methods[id] = true;
7826 node.add(this.match('('));
7835 if(this.look.content() != ')') {
7841 node.add(this.fnparams());
785 }
7865 node.add(
787 this.match(')'),
788 this.match('{'),
789 this.fnbody(true),
790 this.match('}', 'missing } in compound statement')
791 );
792 }
7937 return node;
794 },
795 expr: function(noIn) {
79611059 var node = new Node(Node.EXPR),
797 assignexpr = this.assignexpr(noIn);
79811046 if(this.look && this.look.content() == ',') {
799540 node.add(assignexpr);
800540 while(this.look && this.look.content() == ',') {
8011051 node.add(this.match(), this.assignexpr(noIn));
802 }
803 }
804 else {
80510506 return assignexpr;
806 }
807540 return node;
808 },
809 assignexpr: function(noIn) {
81030787 var node = new Node(Node.ASSIGNEXPR),
811 cndt = this.cndtexpr(noIn);
81230774 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())) {
8263675 node.add(cndt, this.match(), this.assignexpr(noIn));
827 }
828 else {
82927099 return cndt;
830 }
8313675 return node;
832 },
833 cndtexpr: function(noIn) {
83430787 var node = new Node(Node.CNDTEXPR),
835 logorexpr = this.logorexpr(noIn);
83630774 if(this.look && this.look.content() == '?') {
837878 node.add(
838 logorexpr,
839 this.match(),
840 this.assignexpr(noIn),
841 this.match(':'),
842 this.assignexpr(noIn)
843 );
844 }
845 else {
84629896 return logorexpr;
847 }
848878 return node;
849 },
850 logorexpr: function(noIn) {
85130787 var node = new Node(Node.LOGOREXPR),
852 logandexpr = this.logandexpr(noIn);
85330774 if(this.look && this.look.content() == '||') {
8541015 node.add(logandexpr);
8551015 while(this.look && this.look.content() == '||') {
8561150 node.add(
857 this.match(),
858 this.logandexpr(noIn)
859 );
860 }
861 }
862 else {
86329759 return logandexpr;
864 }
8651015 return node;
866 },
867 logandexpr: function(noIn) {
86831937 var node = new Node(Node.LOGANDEXPR),
869 bitorexpr = this.bitorexpr(noIn);
87031924 if(this.look && this.look.content() == '&&') {
8711129 node.add(bitorexpr);
8721129 while(this.look && this.look.content() == '&&') {
8731375 node.add(
874 this.match(),
875 this.bitorexpr(noIn)
876 );
877 }
878 }
879 else {
88030795 return bitorexpr;
881 }
8821129 return node;
883 },
884 bitorexpr: function(noIn) {
88533312 var node = new Node(Node.BITOREXPR),
886 bitxorexpr = this.bitxorexpr(noIn);
88733299 if(this.look && this.look.content() == '|') {
8885 node.add(bitxorexpr);
8895 while(this.look && this.look.content() == '|') {
8905 node.add(
891 this.match(),
892 this.bitxorexpr(noIn)
893 );
894 }
895 }
896 else {
89733294 return bitxorexpr;
898 }
8995 return node;
900 },
901 bitxorexpr: function(noIn) {
90233317 var node = new Node(Node.BITXOREXPR),
903 bitandexpr = this.bitandexpr(noIn);
90433304 if(this.look && this.look.content() == '^') {
9052 node.add(bitandexpr);
9062 while(this.look && this.look.content() == '^') {
9072 node.add(
908 this.match(),
909 this.bitandexpr(noIn)
910 );
911 }
912 }
913 else {
91433302 return bitandexpr;
915 }
9162 return node;
917 },
918 bitandexpr: function(noIn) {
91933319 var node = new Node(Node.BITANDEXPR),
920 eqexpr = this.eqexpr(noIn);
92133306 if(this.look && this.look.content() == '&') {
92218 node.add(eqexpr);
92318 while(this.look && this.look.content() == '&') {
92418 node.add(
925 this.match(),
926 this.eqexpr(noIn)
927 );
928 }
929 }
930 else {
93133288 return eqexpr;
932 }
93318 return node;
934 },
935 eqexpr: function(noIn) {
93633337 var node = new Node(Node.EQEXPR),
937 reltexpr = this.reltexpr(noIn);
93833324 if(this.look && {
939 '==': true,
940 '===': true,
941 '!==': true,
942 '!=': true
943 }.hasOwnProperty(this.look.content())) {
9441589 node.add(reltexpr);
9451589 while(this.look && {
946 '==': true,
947 '===': true,
948 '!==': true,
949 '!=': true
950 }.hasOwnProperty(this.look.content())) {
9511590 node.add(
952 this.match(),
953 this.reltexpr(noIn)
954 );
955 }
956 }
957 else {
95831735 return reltexpr;
959 }
9601589 return node;
961 },
962 reltexpr: function(noIn) {
96334927 var node = new Node(Node.RELTEXPR),
964 shiftexpr = this.shiftexpr();
96534914 if(this.look && ({
966 '<': true,
967 '>': true,
968 '>=': true,
969 '<=': true,
970 'instanceof': true
971 }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
972477 node.add(shiftexpr);
973477 while(this.look && ({
974 '<': true,
975 '>': true,
976 '>=': true,
977 '<=': true,
978 'instanceof': true
979 }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
980477 node.add(
981 this.match(),
982 this.shiftexpr()
983 );
984 }
985 }
986 else {
98734437 return shiftexpr;
988 }
989477 return node;
990 },
991 shiftexpr: function() {
99235404 var node = new Node(Node.SHIFTEXPR),
993 addexpr = this.addexpr();
99435391 if(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
9958 node.add(addexpr);
9968 while(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
9979 node.add(
998 this.match(),
999 this.addexpr()
1000 );
1001 }
1002 }
1003 else {
100435383 return addexpr;
1005 }
10068 return node;
1007 },
1008 addexpr: function() {
100935413 var node = new Node(Node.ADDEXPR),
1010 mtplexpr = this.mtplexpr();
101135400 if(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
1012777 node.add(mtplexpr);
1013777 while(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
10141318 node.add(
1015 this.match(),
1016 this.mtplexpr()
1017 );
1018 }
1019 }
1020 else {
102134623 return mtplexpr;
1022 }
1023777 return node;
1024 },
1025 mtplexpr: function() {
102636731 var node = new Node(Node.MTPLEXPR),
1027 unaryexpr = this.unaryexpr();
102836718 if(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
102951 node.add(unaryexpr);
103051 while(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
103152 node.add(
1032 this.match(),
1033 this.unaryexpr()
1034 );
1035 }
1036 }
1037 else {
103836667 return unaryexpr;
1039 }
104051 return node;
1041 },
1042 unaryexpr: function() {
104338501 var node = new Node(Node.UNARYEXPR);
104438501 if(!this.look) {
10452 this.error();
1046 }
104738499 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 '!':
10571718 node.add(
1058 this.match(),
1059 this.unaryexpr()
1060 );
10611716 break;
1062 default:
106336781 return this.postfixexpr();
1064 }
10651716 return node;
1066 },
1067 postfixexpr: function() {
106836781 var node = new Node(Node.POSTFIXEXPR);
106936781 var leftexpr = this.leftexpr();
107036770 if(this.look && ['++', '--'].indexOf(this.look.content()) > -1 && !this.hasMoveLine) {
1071327 node.add(leftexpr);
1072327 while(this.look && ['++', '--'].indexOf(this.look.content()) > -1) {
1073327 node.add(this.match(undefined, true));
1074 }
1075 }
1076 else {
107736443 return leftexpr;
1078 }
1079327 return node;
1080 },
1081 leftexpr: function() {
108236781 if(this.look.content() == 'new') {
1083190 return this.newexpr();
1084 }
1085 else {
108636591 return this.callexpr();
1087 }
1088 },
1089 newexpr: function(depth) {
1090191 depth = depth || 0;
1091191 var node = new Node(Node.NEWEXPR);
1092191 node.add(this.match('new'));
1093191 if(!this.look) {
10941 this.error();
1095 }
1096190 if(this.look.content() == 'new') {
10971 node.add(this.newexpr(depth + 1));
1098 }
1099 else {
1100189 node.add(this.mmbexpr());
1101 }
1102190 if(this.look && this.look.content() == '(') {
1103179 node.add(this.args());
1104 }
1105190 if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
110611 var mmb = new Node(Node.MMBEXPR);
110711 mmb.add(node);
110811 while(this.look) {
110920 if(this.look.content() == '.') {
111010 mmb.add(
1111 this.match(),
1112 this.match(Token.ID)
1113 );
1114 }
111510 else if(this.look.content() == '[') {
11161 mmb.add(
1117 this.match(),
1118 this.expr(),
1119 this.match(']')
1120 );
1121 }
1122 else {
11239 break;
1124 }
1125 }
112611 if(depth == 0 && this.look && this.look.content() == '(') {
11278 var callexpr = this.callexpr(mmb);
11288 return callexpr;
1129 }
11303 return mmb;
1131 }
1132179 return node;
1133 },
1134 callexpr: function(mmb) {
113536599 var node = new Node(Node.CALLEXPR);
113636599 mmb = mmb || this.mmbexpr();
113736590 if(this.look && this.look.content() == '(') {
11385159 node.add(
1139 mmb,
1140 this.args()
1141 );
11425158 if(this.look && ['.', '[', '('].indexOf(this.look.content()) > -1) {
1143364 while(this.look) {
11441186 if(this.look.content() == '.') {
1145426 node.add(
1146 this.match(),
1147 this.match(Token.ID)
1148 );
1149 }
1150760 else if(this.look.content() == '[') {
115145 node.add(
1152 this.match(),
1153 this.expr(),
1154 this.match(']')
1155 );
1156 }
1157715 else if(this.look.content() == '(') {
1158351 node.add(this.args());
1159 }
1160 else {
1161364 break;
1162 }
1163 }
1164 }
1165 }
1166 else {
116731431 return mmb;
1168 }
11695158 return node;
1170 },
1171 mmbexpr: function() {
117236780 var node = new Node(Node.MMBEXPR);
117336780 var mmb;
117436780 if(this.look.content() == 'function') {
11751625 mmb = this.fnexpr();
1176 }
1177 else {
117835155 mmb = this.prmrexpr();
1179 }
118036771 if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
118110738 node.add(mmb);
118210738 while(this.look) {
118323521 if(this.look.content() == '.') {
118410860 node.add(
1185 this.match(),
1186 this.match(Token.ID)
1187 );
1188 }
118912661 else if(this.look.content() == '[') {
11901928 node.add(
1191 this.match(),
1192 this.expr(),
1193 this.match(']')
1194 );
1195 }
1196 else {
119710733 break;
1198 }
1199 }
1200 }
1201 else {
120226033 return mmb;
1203 }
120410738 return node;
1205 },
1206 prmrexpr: function() {
120735155 var node = new Node(Node.PRMREXPR);
120835155 switch(this.look.type()) {
1209 case Token.ID:
1210 case Token.NUMBER:
1211 case Token.STRING:
1212 case Token.REG:
1213 case Token.TEMPLATE:
121429080 node.add(this.match());
121529080 break;
1216 default:
12176075 switch(this.look.content()) {
1218 case 'this':
1219 case 'null':
1220 case 'true':
1221 case 'false':
12223381 node.add(this.match());
12233381 break;
1224 case '(':
12251087 node.add(this.match(), this.expr(), this.match(')'));
12261083 break;
1227 case '[':
1228876 node.add(this.arrltr());
1229876 break;
1230 case '{':
1231730 node.add(this.objltr());
1232730 break;
1233 default:
12341 this.error();
1235 }
1236 }
123735150 return node;
1238 },
1239 bindid: function() {
12402 var node = new Node(Node.BINDID);
12412 node.add(this.match('...'), this.assignexpr());
12422 return node;
1243 },
1244 arrltr: function() {
1245876 var node = new Node(Node.ARRLTR);
1246876 node.add(this.match('['));
1247876 while(this.look && this.look.content() != ']' && this.look.content() != '...') {
12481518 if(this.look.content() == ',') {
12494 node.add(this.match());
1250 }
1251 else {
12521514 node.add(this.assignexpr());
12531514 if(this.look && this.look.content() == ',') {
1254871 node.add(this.match());
1255 }
1256 }
1257 }
1258876 if(this.look.content() == '...') {
12591 node.add(this.spread());
1260 }
1261876 node.add(this.match(']', 'missing ] after element list'));
1262876 return node;
1263 },
1264 spread: function() {
12651 var node = new Node(Node.SPREAD);
12661 node.add(this.match('...'), this.assignexpr());
12671 return node;
1268 },
1269 objltr: function() {
1270730 var node = new Node(Node.OBJLTR);
1271730 node.add(this.match('{'));
1272730 while(this.look && this.look.content() != '}') {
12732002 node.add(this.proptassign());
12742002 if(this.look && this.look.content() == ',') {
12751511 node.add(this.match());
1276 }
1277 }
1278730 node.add(this.match('}', 'missing } after property list'));
1279730 return node;
1280 },
1281 proptassign: function() {
12822002 var node = new Node(Node.PROPTASSIGN);
12832002 if(!this.look) {
12840 this.error();
1285 }
12862002 if(this.look.content() == 'get') {
128728 node.add(this.match());
128828 if(!this.look) {
12890 this.error();
1290 }
129128 if(this.look.content() == ':') {
129228 node.add(this.match(), this.assignexpr());
1293 }
1294 else {
12950 node.add(this.getfn());
1296 }
1297 }
12981974 else if(this.look.content() == 'set') {
129930 node.add(this.match());
130030 if(!this.look) {
13010 this.error();
1302 }
130330 if(this.look.content() == ':') {
130430 node.add(this.match(), this.assignexpr());
1305 }
1306 else {
13070 node.add(this.setfn());
1308 }
1309 }
1310 else {
13111944 switch(this.look.type()) {
1312 case Token.ID:
1313 case Token.STRING:
1314 case Token.NUMBER:
13151944 node.add(
1316 this.match(),
1317 this.match(':', 'missing : after property id'),
1318 this.assignexpr()
1319 );
13201944 break;
1321 default:
13220 this.error('invalid property id');
1323 }
1324 }
13252002 return node;
1326 },
1327 getfn: function() {
13281 var node = new Node(Node.GETFN);
13291 node.add(
1330 this.proptname(),
1331 this.match('('),
1332 this.match(')'),
1333 this.match('{'),
1334 this.fnbody(),
1335 this.match('}')
1336 );
13371 return node;
1338 },
1339 setfn: function() {
13401 var node = new Node(Node.SETFN);
13411 node.add(
1342 this.proptname(),
1343 this.match('('),
1344 this.propsets(),
1345 this.match(')'),
1346 this.match('{'),
1347 this.fnbody(),
1348 this.match('}')
1349 );
13501 return node;
1351 },
1352 proptname: function() {
13532 var node = new Node(Node.PROPTNAME);
13542 if(this.look) {
13552 switch(this.look.type()) {
1356 case Token.ID:
1357 case Token.NUMBER:
1358 case Token.STRING:
13592 node.add(this.match());
13602 break;
1361 default:
13620 this.error('missing name after . operator');
1363 }
1364 }
13652 return node;
1366 },
1367 propsets: function() {
13681 var node = new Node(Node.PROPTSETS);
13691 node.add(this.match(Token.ID, 'setter functions must have one argument'));
13701 return node;
1371 },
1372 args: function() {
13735691 var node = new Node(Node.ARGS);
13745691 node.add(this.match('('));
13755691 if(!this.look) {
13761 this.error();
1377 }
13785690 if(this.look.content() != ')') {
13794949 node.add(this.arglist());
1380 }
13815690 node.add(this.match(')'));
13825690 return node;
1383 },
1384 arglist: function() {
13854949 var node = new Node(Node.ARGLIST);
13864949 while(this.look && this.look.content() != ')' && this.look.content() != '...') {
13877795 node.add(this.assignexpr());
13887795 if(this.look && this.look.content() == ',') {
13892848 node.add(this.match());
1390 }
1391 }
13924949 if(this.look && this.look.content() == '...') {
13932 node.add(this.bindid());
1394 }
13954949 return node;
1396 },
1397 virtual: function(s) {
13981590 return new Node(Node.TOKEN, new Token(Token.VIRTUAL, s));
1399 },
1400 match: function(type, line, msg) {
1401140306 if(typeof type == 'boolean') {
14020 msg = line;
14030 line = type;
14040 type = undefined;
1405 }
1406140306 if(typeof line != 'boolean') {
1407138163 line = false;
1408138163 msg = line;
1409 }
1410 //未定义为所有非空白token
1411140306 if(character.isUndefined(type)) {
141270821 if(this.look) {
141370821 var l = this.look;
141470821 this.move(line);
141570821 return new Node(Node.TOKEN, l);
1416 }
1417 else {
14180 this.error('syntax error' + (msg || ''));
1419 }
1420 }
1421 //或者根据token的type或者content匹配
142269485 else if(typeof type == 'string') {
1423 //特殊处理;,不匹配但有换行或者末尾时自动补全,还有受限行
142452798 if(type == ';'
1425 && (!this.look
1426 || (this.look.content() != type && this.hasMoveLine)
1427 || this.look.content() == '}')
1428 ) {
14291590 if(this.look && S[this.look.type()]) {
143036 this.move();
1431 }
14321590 return this.virtual(';');
1433 }
143451208 else if(this.look && this.look.content() == type) {
143551205 var l = this.look;
143651205 this.move(line);
143751205 return new Node(Node.TOKEN, l);
1438 }
1439 else {
14403 this.error('missing ' + type + (msg || ''));
1441 }
1442 }
144316687 else if(typeof type == 'number') {
144416687 if(this.look && this.look.type() == type) {
144516686 var l = this.look;
144616686 this.move(line);
144716686 return new Node(Node.TOKEN, l);
1448 }
1449 else {
14501 this.error('missing ' + Token.type(type) + (msg || ''));
1451 }
1452 }
1453 },
1454 move: function(line) {
1455138942 this.lastLine = this.line;
1456138942 this.lastCol = this.col;
1457 //遗留下来的换行符
1458138942 this.hasMoveLine = false;
1459138942 do {
1460248200 this.look = this.tokens[this.index++];
1461248200 if(!this.look) {
1462184 return;
1463 }
1464 //存下忽略的token
1465248016 if(S[this.look.type()]) {
1466109294 this.ignores[this.index - 1] = this.look;
1467 }
1468 //包括line的情况下要跳出
1469248016 if(this.look.type() == Token.LINE) {
147017784 this.line++;
147117784 this.col = 1;
147217784 this.hasMoveLine = true;
147317784 if(line) {
147435 break;
1475 }
1476 }
1477230232 else if(this.look.type() == Token.COMMENT) {
14782742 var s = this.look.content();
14792742 var n = character.count(this.look.content(), character.LINE);
14802742 if(n > 0) {
148190 this.line += n;
148290 var i = s.lastIndexOf(character.LINE);
148390 this.col += s.length - i - 1;
148490 this.hasMoveLine = true;
148590 if(line) {
14861 break;
1487 }
1488 }
1489 }
1490 else {
1491227490 this.col += this.look.content().length;
1492227490 if(!S[this.look.type()]) {
1493138722 break;
1494 }
1495 }
1496 } while(this.index <= this.length);
1497 },
1498 error: function(msg) {
149933 msg = 'SyntaxError: ' + (msg || ' syntax error');
150033 throw new Error(msg + ' line ' + this.lastLine + ' col ' + this.lastCol);
1501 },
1502 ignore: function() {
15038 return this.ignores;
1504 }
1505});
15061module.exports = Parser;

/Users/army/Sites/homunculus/src/util/Class.js

100%
24
24
0
LineHitsSource
11function inheritPrototype(subType, superType) {
210 var prototype = Object.create(superType.prototype);
310 prototype.constructor = subType;
410 subType.prototype = prototype;
5 //继承static变量
610 Object.keys(superType).forEach(function(k) {
737 subType[k] = superType[k];
8 });
910 return subType;
10}
111function wrap(fn) {
1219 fn.extend = function(sub) {
1310 inheritPrototype(sub, fn);
1410 return wrap(sub);
15 }
1619 fn.methods = function(o) {
1716 Object.keys(o).forEach(function(k) {
18201 fn.prototype[k] = o[k];
19 });
2016 return fn;
21 };
2219 fn.statics = function(o) {
238 Object.keys(o).forEach(function(k) {
24143 fn[k] = o[k];
25 });
268 return fn;
27 };
2819 return fn;
29}
301function klass(cons) {
319 return wrap(cons || function() {});
32}
331klass.extend = inheritPrototype;
341module.exports = klass;

/Users/army/Sites/homunculus/src/util/character.js

87%
41
36
5
LineHitsSource
11exports.LINE = '\n';
21exports.ENTER = '\r';
31exports.BLANK = ' ';
41exports.TAB = '\t';
51exports.UNDERLINE = '_';
61exports.DOLLAR = '$';
71exports.SHARP = '#';
81exports.MINUS = '-';
91exports.AT = '@';
101exports.SLASH = '/';
111exports.BACK_SLASH = '\\';
121exports.DECIMAL = '.';
131exports.LEFT_BRACKET = '[';
141exports.RIGHT_BRACKET = ']';
151exports.STAR = '*';
161exports.LEFT_PARENTHESE = '(';
171exports.RIGHT_PARENTHESE = ')';
181exports.COMMA = ',';
191exports.SEMICOLON = ';';
201exports.EQUAL = '=';
211exports.isDigit = function(c) {
220 return c >= '0' && c <= '9';
23};
241exports.isDigit16 = function(c) {
250 return exports.isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
26};
271exports.isDigit2 = function(c) {
280 return c == '0' || c == '1';
29};
301exports.isDigit8 = function(c) {
310 return c >= '0' && c <= '7';
32};
331exports.isLetter = function(c) {
34691 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
35};
361exports.count = function(s, c) {
37457060 var count = 0,
38 i = -1;
39457060 while((i = s.indexOf(c, i + 1)) != -1) {
4033562 count++;
41 }
42457060 return count;
43};
441exports.isUndefined = function(s) {
454427744 return typeof s == 'undefined';
46};
471exports.isString = function(s) {
480 return Object.prototype.toString.call(s) == "[object String]";
49};
501exports.isNumber = function(s) {
51456402 return Object.prototype.toString.call(s) == "[object Number]";
52};
\ No newline at end of file +

Coverage

98%
1195
1181
14

/Users/army/Sites/homunculus/src/lexer/Lexer.js

100%
128
128
0
LineHitsSource
11var Class = require('../util/Class');
21var character = require('../util/character');
31var Token = require('./Token');
41var Lexer = Class(function(rule) {
5245 this.rule = rule; //当前语法规则
6245 this.init();
7}).methods({
8 init: function() {
9246 this.code = ''; //要解析的代码
10246 this.peek = ''; //向前看字符
11246 this.index = 0; //向前看字符字符索引
12246 this.isReg = Lexer.IS_REG; //当前/是否是perl风格正则表达式
13246 this.tokenList = []; //结果的token列表
14246 this.parentheseState = false; //(开始时标记之前终结符是否为if/for/while等关键字
15246 this.parentheseStack = []; //圆括号深度记录当前是否为if/for/while等语句内部
16246 this.cacheLine = 0; //行缓存值
17246 this.totalLine = 1; //总行数
18246 this.colNum = 0; //列
19246 this.colMax = 0; //最大列数
20 },
21 parse: function(code) {
22202 this.code = code || '';
23202 var temp = [];
24202 this.scan(temp);
25193 return temp;
26 },
27 parseOn: function() {
281 var temp = [];
291 this.scan(temp);
301 return temp;
31 },
32 tokens: function() {
33167 return this.tokenList;
34 },
35 scan: function(temp) {
36203 var perlReg = this.rule.perlReg();
37203 var length = this.code.length;
38203 var count = 0;
39 outer:
40 while(this.index < length) {
41454460 if(this.cacheLine > 0 && count >= this.cacheLine) {
421 break;
43 }
44454459 this.readch();
45 //perl风格正则
46454459 if(perlReg && this.isReg == Lexer.IS_REG && this.peek == character.SLASH && !{ '/': true, '*': true }[this.code.charAt(this.index)]) {
47493 this.dealReg(temp, length);
48489 this.isReg = Lexer.NOT_REG;
49 }
50 //依次遍历匹配规则,命中则继续
51 else {
52453966 for(var i = 0, matches = this.rule.matches(), len = matches.length; i < len; i++) {
537880077 var match = matches[i];
547880077 if(match.match(this.peek, this.code, this.index)) {
55453965 var token = new Token(match.tokenType(), match.content(), match.val(), this.index - 1);
56453965 var error = match.error();
57453965 var matchLen = match.content().length;
58453965 if(token.type() == Token.ID && this.rule.keyWords().hasOwnProperty(token.content())) {
5921465 token.type(Token.KEYWORD);
60 }
61453965 temp.push(token);
62453965 this.tokenList.push(token);
63453965 this.index += matchLen - 1;
64453965 var n = character.count(token.val(), character.LINE);
65453965 count += n;
66453965 this.totalLine += n;
67453965 if(n) {
6832320 var j = match.content().indexOf(character.LINE);
6932320 var k = match.content().lastIndexOf(character.LINE);
7032320 this.colMax = Math.max(this.colMax, this.colNum + j);
7132320 this.colNum = match.content().length - k;
72 }
73 else {
74421645 this.colNum += matchLen;
75 }
76453965 this.colMax = Math.max(this.colMax, this.colNum);
77453965 if(error) {
785 this.error(error, this.code.slice(this.index - matchLen, this.index));
79 }
80 //支持perl正则需判断关键字、圆括号对除号语义的影响
81453961 if(perlReg && match.perlReg() != Lexer.IGNORE) {
82258227 if(match.perlReg() == Lexer.SPECIAL) {
8396002 this.isReg = match.special();
84 }
85 else {
86162225 this.isReg = match.perlReg();
87 }
88258227 if(this.peek == character.LEFT_PARENTHESE) {
8919875 this.parentheseStack.push(this.parentheseState);
9019875 this.parentheseState = false;
91 }
92238352 else if(this.peek == character.RIGHT_PARENTHESE) {
9319858 this.isReg = this.parentheseStack.pop() ? Lexer.IS_REG : Lexer.NOT_REG;
94 }
95 else {
96218494 this.parentheseState = match.parenthese();
97 }
98 }
99453961 continue outer;
100 }
101 }
102 //如果有未匹配的,说明规则不完整,抛出错误
1031 this.error('unknow token');
104 }
105 }
106194 return this;
107 },
108 readch: function() {
109463321 this.peek = this.code.charAt(this.index++);
110 },
111 dealReg: function(temp, length) {
112493 var lastIndex = this.index - 1;
113493 var res = false;
114 outer:
115 do {
1166803 this.readch();
1176803 if(this.peek == character.LINE) {
1182 this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index));
1191 break;
120 }
1216801 else if(this.peek == character.BACK_SLASH) {
122490 this.index++;
123 }
1246311 else if(this.peek == character.LEFT_BRACKET) {
125264 do {
1261368 this.readch();
1271368 if(this.peek == character.LINE) {
1282 this.error('SyntaxError: unterminated regular expression literal ' + this.peek, this.code.slice(lastIndex, this.index));
1291 break outer;
130 }
1311366 else if(this.peek == character.BACK_SLASH) {
132265 this.index++;
133 }
1341101 else if(this.peek == character.RIGHT_BRACKET) {
135262 continue outer;
136 }
137 } while(this.index < length);
138 }
1396047 else if(this.peek == character.SLASH) {
140488 res = true;
141488 var hash = {};
142488 var flag = {
143 'g': true,
144 'i': true,
145 'm': true,
146 'y': true
147 };
148 //正则的flag中有gimy4种,大小写敏感且不能重复
149488 do {
150691 this.readch();
151691 if(character.isLetter(this.peek)) {
152205 if(hash.hasOwnProperty(this.peek) || !flag.hasOwnProperty(this.peek)) {
1532 this.error('SyntaxError: invalid regular expression flag ' + this.peek, this.code.slice(lastIndex, this.index));
1541 break outer;
155 }
156203 hash[this.peek] = true;
157 }
158 else {
159486 break outer;
160 }
161 } while(this.index <= length);
162 }
163 } while(this.index < length);
164490 if(!res) {
1653 this.error('SyntaxError: unterminated regular expression literal', this.code.slice(lastIndex, this.index - 1));
166 }
167489 var token = new Token(Token.REG, this.code.slice(lastIndex, --this.index), lastIndex);
168489 temp.push(token);
169489 this.tokenList.push(token);
170489 this.colNum += this.index - lastIndex;
171489 this.colMax = Math.max(this.colMax, this.colNum);
172489 return this;
173 },
174 cache: function(i) {
1751 if(!character.isUndefined(i) && i !== null) {
1761 this.cacheLine = i;
177 }
1781 return this.cacheLine;
179 },
180 finish: function() {
1812 return this.index >= this.code.length;
182 },
183 line: function() {
18416 return this.totalLine;
185 },
186 col: function() {
1871 return this.colMax;
188 },
189 error: function(s, str) {
19015 if(character.isUndefined(str)) {
1911 str = this.code.substr(this.index - 1, 20);
192 }
19315 if(Lexer.mode() === Lexer.STRICT) {
1949 throw new Error(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str);
195 }
1966 else if(Lexer.mode() === Lexer.LOOSE && !character.isUndefined(console)) {
1976 if(console.warn) {
1986 console.warn(s + ', line ' + this.line() + ' col ' + this.colNum + '\n' + str);
199 }
200 }
2016 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) {
21133 if(!character.isUndefined(i)) {
21210 cmode = i;
213 }
21433 return cmode;
215 }
216});
2171var cmode = Lexer.STRICT;
2181module.exports = Lexer;

/Users/army/Sites/homunculus/src/lexer/Token.js

100%
41
41
0
LineHitsSource
11var Class = require('../util/Class');
21var character = require('../util/character');
31var tid = 0;
41var types;
51var Token = Class(function(type, content, val, sIndex) {
6456037 this.t = type; //token类型
7456037 this.c = content; //token的字面内容,string包括头尾的引号
8456037 if(character.isNumber(val)) {
9494 sIndex = val;
10494 val = content;
11 }
12455543 else if(character.isUndefined(val)) {
131578 val = content;
141578 sIndex = -1;
15 }
16456037 this.v = val; //token的值,一般情况下等于content,特殊如string情况下值是不加头尾的引号
17456037 this.id = tid++; //token的索引
18456037 this.si = sIndex; //token在源码字符串中的索引
19}).methods({
20 type: function(t) {
211635737 if(!character.isUndefined(t)) {
2221465 this.t = t;
23 }
241635737 return this.t;
25 },
26 content: function(c) {
271670740 if(!character.isUndefined(c)) {
281 this.c = c;
29 }
301670740 return this.c;
31 },
32 val: function(v) {
33453967 if(!character.isUndefined(v)) {
341 this.v = v;
35 }
36453967 return this.v;
37 },
38 tag: function(t) {
392 if(!character.isUndefined(t)) {
401 this.t = t;
41 }
422 return Token.type(this.t);
43 },
44 tid: function(id) {
452 if(!character.isUndefined(id)) {
461 this.id = id;
47 }
482 return this.id;
49 },
50 sIndex: function(si) {
516 if(!character.isUndefined(si)) {
521 this.si = si;
53 }
546 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) {
797 if(character.isUndefined(types)) {
801 types = [];
811 Object.keys(Token).forEach(function(o) {
8225 if(typeof Token[o] == 'number') {
8321 types[Token[o]] = o;
84 }
85 });
86 }
877 return types[tag];
88 }
89});
901module.exports = Token;

/Users/army/Sites/homunculus/src/lexer/match/CharacterSet.js

100%
10
10
0
LineHitsSource
11var Match = require('./Match');
21var character = require('../../util/character');
31var CharacterSet = Match.extend(function(type, str, setPReg) {
4486 Match.call(this, type, setPReg);
5486 this.str = str;
6}).methods({
7 match: function(c, code, index) {
8140961 var isIn = this.str.indexOf(c) > -1;
9140961 if(isIn) {
10133444 this.result = c;
11 }
12140961 return isIn;
13 }
14});
151module.exports = CharacterSet;

/Users/army/Sites/homunculus/src/lexer/match/CompleteEqual.js

100%
7
7
0
LineHitsSource
11var Match = require('./Match');
21var character = require('../../util/character');
31var CompleteEqual = Match.extend(function(type, result, setPReg) {
49119 Match.call(this, type, setPReg);
59119 this.result = result;
6}).methods({
7 match: function(c, code, index) {
86032949 return code.substr(--index, this.result.length) == this.result;
9 }
10});
111module.exports = CompleteEqual;

/Users/army/Sites/homunculus/src/lexer/match/LineParse.js

96%
32
31
1
LineHitsSource
11var Match = require('./Match');
21var Token = require('../Token');
31var character = require('../../util/character');
41var LineParse = Match.extend(function(type, begin, end, mutiline, setPReg) {
5733 if(character.isUndefined(mutiline)) {
60 mutiline = false;
7 }
8733 Match.call(this, type, setPReg);
9733 this.begin = begin;
10733 this.end = end;
11733 this.msg = null;
12733 this.mutiline = mutiline;
13 }).methods({
14 match: function(c, code, index) {
15762664 this.msg = null;
16762664 if(this.begin == code.charAt(index - 1)) {
176828 var len = code.length,
18 lastIndex = index - 1,
19 res = false;
206828 while(index < len) {
2157360 var c = code.charAt(index++);
22 //转义
2357360 if(c == character.BACK_SLASH) {
24402 if(code.charAt(index++) == character.ENTER) {
251 index++;
26 }
27 }
2856958 else if(c == character.LINE && !this.mutiline) {
291 break;
30 }
3156957 else if(c == this.end) {
326826 res = true;
336826 break;
34 }
35 }
366828 if(!res) {
372 this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase() + ' literal';
38 }
396828 this.result = code.slice(lastIndex, index);
406828 return true;
41 }
42755836 return false;
43 },
44 error: function() {
456828 return this.msg;
46 },
47 val: function() {
486828 return this.content().slice(this.begin.length, -this.end.length);
49 }
50 });
511module.exports = LineParse;

/Users/army/Sites/homunculus/src/lexer/match/LineSearch.js

100%
33
33
0
LineHitsSource
11var Match = require('./Match');
21var Token = require('../Token');
31var character = require('../../util/character');
41var LineSearch = Match.extend(function(type, begin, end, contain, setPReg) {
5490 if(character.isUndefined(contain)) {
6245 contain = false;
7 }
8490 Match.call(this, type, setPReg);
9490 this.begin = begin;
10490 this.end = end;
11490 this.contain = contain;
12490 this.msg = null;
13}).methods({
14 match: function(c, code, index) {
15521678 this.msg = null;
16521678 if(this.begin == code.substr(--index, this.begin.length)) {
17 //支持多个end匹配时不支持包含选项
185001 if(!this.contain && Array.isArray(this.end)) {
194786 for(var j = 0, len = this.end.length; j < len; j++) {
2014358 var i = code.indexOf(this.end[j], index + this.begin.length);
2114358 if(i != -1) {
224785 this.result = code.slice(index, i);
234785 return true;
24 }
25 }
26 //都不匹配时到末尾
271 this.result = code.slice(index);
281 return true;
29 }
30 else {
31215 var i = code.indexOf(this.end, index + this.begin.length);
32215 if(i == -1) {
331 if(this.contain) {
341 this.msg = 'SyntaxError: unterminated ' + Token.type(this.type).toLowerCase();
35 }
361 i = code.length;
37 }
38214 else if(this.contain) {
39214 i += this.end.length;
40 }
41215 this.result = code.slice(index, i);
42215 return true;
43 }
44 }
45516677 return false;
46 },
47 error: function() {
485001 return this.msg;
49 }
50});
511module.exports = LineSearch;

/Users/army/Sites/homunculus/src/lexer/match/Match.js

91%
24
22
2
LineHitsSource
11var Class = require('../../util/Class');
21var character = require('../../util/character');
31var Lexer = require('../Lexer');
41module.exports = Class(function(type, setPReg, special, parenthese) {
511828 this.type = type;
611828 if(character.isUndefined(setPReg)) {
72594 setPReg = Lexer.IGNORE;
8 }
911828 this.setPReg = setPReg;
1011828 this.result = null;
11 //忽略0,是1,否2,特殊3
1211828 if(setPReg) {
139234 if(character.isUndefined(special)) {
148991 special = function() {
150 return Lexer.IGNORE;
16 };
17 }
189234 if(character.isUndefined(parenthese)) {
198991 parenthese = function() {
20122492 return false;
21 };
22 }
23 }
2411828 this.special = special;
2511828 this.parenthese = parenthese;
26}).methods({
27 tokenType: function() {
28453965 return this.type;
29 },
30 perlReg: function() {
31874413 return this.setPReg;
32 },
33 val: function() {
34447137 return this.content();
35 },
36 content: function() {
371650859 return this.result;
38 },
39 match: function(c, code, index) {
40 //需被实现
410 throw new Error('match needs to be implement');
42 },
43 error: function() {
44338611 return false;
45 }
46});

/Users/army/Sites/homunculus/src/lexer/match/RegMatch.js

100%
23
23
0
LineHitsSource
11var Match = require('./Match');
21var RegMatch = Match.extend(function(type, reg, valid, setPReg, special, parenthese) {
31000 if(typeof valid == 'number') {
4243 parenthese = special;
5243 special = setPReg;
6243 setPReg = valid;
7243 valid = null;
8 }
91000 Match.call(this, type, setPReg, special, parenthese);
101000 this.reg = reg;
111000 this.valid = valid;
12 }).methods({
13 match: function(c, code, index) {
14421825 var self = this,
15 res = self.reg.exec(code.slice(index - 1));
16421825 self.msg = null;
17421825 if(res) {
18103525 self.result = res[0];
19103525 if(self.valid) {
207523 for(var i = 0, keys = Object.keys(self.valid), len = keys.length; i < len; i++) {
217523 if(self.valid[keys[i]].test(self.result)) {
222 self.msg = keys[i];
232 break;
24 }
25 }
26 }
27103525 return true;
28 }
29318300 return false;
30 },
31 error: function() {
32103525 return this.msg;
33 }
34 });
351module.exports = RegMatch;

/Users/army/Sites/homunculus/src/lexer/rule/EcmascriptRule.js

100%
36
36
0
LineHitsSource
11var Rule = require('./Rule');
21var LineSearch = require('../match/LineSearch');
31var LineParse = require('../match/LineParse');
41var CompleteEqual = require('../match/CompleteEqual');
51var RegMatch = require('../match/RegMatch');
61var CharacterSet = require('../match/CharacterSet');
71var Token = require('../Token');
81var Lexer = require('../Lexer');
91var character = require('../../util/character');
101var EcmascriptRule = Rule.extend(function() {
11243 var self = this;
12243 Rule.call(self, EcmascriptRule.KEYWORDS, true);
13
14243 self.addMatch(new CompleteEqual(Token.BLANK, character.BLANK));
15243 self.addMatch(new CompleteEqual(Token.TAB, character.TAB));
16243 self.addMatch(new CompleteEqual(Token.LINE, character.ENTER + character.LINE));
17243 self.addMatch(new CompleteEqual(Token.LINE, character.ENTER));
18243 self.addMatch(new CompleteEqual(Token.LINE, character.LINE));
19
20243 self.addMatch(new LineSearch(Token.COMMENT, '//', [character.ENTER + character.LINE, character.ENTER, character.LINE]));
21243 self.addMatch(new LineSearch(Token.COMMENT, '/*', '*/', true));
22243 self.addMatch(new LineParse(Token.STRING, '"', '"', false, Lexer.IS_REG));
23243 self.addMatch(new LineParse(Token.STRING, "'", "'", false, Lexer.IS_REG));
24243 self.addMatch(new LineParse(Token.TEMPLATE, '`', '`', true, Lexer.IS_REG));
25
26243 self.addMatch(new RegMatch(Token.ID, /^[$a-zA-Z_][$\w]*/, Lexer.SPECIAL, function() {
2796002 return !!(self.keyWords().hasOwnProperty(this.content()));
28 }, function() {
2996002 return ['if', 'for', 'while'].indexOf(this.content()) != -1;
30 }));
31
32243 self.addMatch(new RegMatch(Token.NUMBER, /^\.\d+(?:E[+-]?\d*)?/i, {
33 'SyntaxError: missing exponent': /E[+-]?$/i
34 }, Lexer.NOT_REG));
35
36243 self.addMatch(new CompleteEqual(Token.SIGN, ']', Lexer.NOT_REG));
37
38243 ['*=', '/=', '+=', '-=', '%=', '^=', '&=', '|=', '&&', '--', '++', '===', '==', '!==', '!=', '||', '>>>=', '<<<=', '<<<', '>>>', '>>=', '<<=', '<<', '>>', '>=', '<=', '...', '?:', '=>'].forEach(function(o) {
397047 self.addMatch(new CompleteEqual(Token.SIGN, o, Lexer.IS_REG));
40 });
41243 self.addMatch(new CharacterSet(Token.SIGN, ':;/?.,[]{}~!^|%=-+*()~><&\\', Lexer.IS_REG));
42
43243 self.addMatch(new RegMatch(Token.NUMBER, /^0x[\da-f]*/i, {
44 "SyntaxError: missing hexadecimal digits after '0x'": /^0x$/i
45 }, Lexer.NOT_REG));
46243 self.addMatch(new RegMatch(Token.NUMBER, /^\d+\.?\d*(?:E[+-]?\d*)?/i, {
47 'SyntaxError: missing exponent': /E[+-]?$/i
48 }, Lexer.NOT_REG));
49
50243 self.addMatch(new CompleteEqual(Token.LINE, '\u2028'));
51243 self.addMatch(new CompleteEqual(Token.LINE, '\u2029'));
52243 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: '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});
561module.exports = EcmascriptRule;

/Users/army/Sites/homunculus/src/lexer/rule/Rule.js

100%
14
14
0
LineHitsSource
11var Class = require('../../util/Class');
21var Rule = Class(function(words, pReg) {
3245 var self = this;
4245 self.kw = {};
5245 words.forEach(function(o) {
611305 self.kw[o] = true;
7 });
8245 self.pReg = pReg || false;
9245 self.matchList = [];
10 }).methods({
11 perlReg: function() {
12203 return this.pReg;
13 },
14 addMatch: function(match) {
1511828 this.matchList.push(match);
1611828 return this;
17 },
18 matches: function() {
19453966 return this.matchList;
20 },
21 keyWords: function() {
22192004 return this.kw;
23 }
24 });
251module.exports = Rule;

/Users/army/Sites/homunculus/src/parser/js/Context.js

99%
202
201
1
LineHitsSource
11var Class = require('../../util/Class');
21var JsNode = require('./Node');
31var Token = require('../../lexer/Token');
41var Parser = require('./Parser');
51var id = 0;
61var Context = Class(function(parent, name) {
765 this.id = id++;
865 this.parser = new Parser();
965 this.parent = parent || null; //父上下文,如果是全局则为空
1065 this.name = name || null; //上下文名称,即函数名,函数表达式为空,全局也为空
1165 this.children = []; //函数声明或函数表达式所产生的上下文
1265 this.childrenMap = Object.create(null); //键是函数名,值是上下文,匿名函数表达式键为cid
1365 this.vars = []; //变量var声明
1465 this.varsMap = Object.create(null); //键为id字面量,值是它的token的节点
1565 this.vardeclMap = Object.create(null); //var赋值记录,优先级vardecl > fndecl > varnodecl
1665 this.params = []; //形参,函数上下文才有,即全局无
1765 this.paramsMap = Object.create(null); //键为id字面量,值是它的token的节点
1865 this.aParams = []; //实参,函数表达式才有
1965 this.vids = []; //上下文环境里用到的变量id
2065 this.vidsMap = Object.create(null); //键为id字面量,值是它的token的节点
2165 this.returns = []; //上下文环境里return语句
2265 this.node = null; //对应的ast的节点
2365 this.thisIs = null; //this指向,仅函数表达式call或apply执行时有用
2465 if(!this.isTop()) {
2526 this.parent.addChild(this);
26 }
27}).methods({
28 parse: function(code) {
2935 var ast;
3035 if(code instanceof JsNode) {
311 ast = code;
32 }
33 else {
3434 ast = this.parser.parse(code);
35 }
3635 recursion(ast, this);
3735 return this;
38 },
39 getId: function() {
4029 return this.id;
41 },
42 getName: function() {
4327 return this.name;
44 },
45 getParent: function() {
462 return this.parent;
47 },
48 isTop: function() {
4966 return !this.parent;
50 },
51 isFnexpr: function() {
521 return !this.isTop() && !this.name;
53 },
54 hasParam: function(p) {
556 return p in this.paramsMap;
56 },
57 getParams: function() {
581 return this.params;
59 },
60 addParam: function(p) {
61 //形参不可能重复,无需判断
6210 this.paramsMap[p] = this.params.length;
6310 this.params.push(p);
6410 return this;
65 },
66 getAParams: function() {
674 return this.aParams;
68 },
69 addAParam: function(ap) {
709 this.aParams.push(ap);
719 return this;
72 },
73 getChild: function(name) {
747 return this.childrenMap[name];
75 },
76 getChildren: function() {
7715 return this.children;
78 },
79 //通过name查找函数声明,id查找表达式
80 hasChild: function(name) {
8148 return name in this.childrenMap;
82 },
83 addChild: function(child) {
8426 var name = child.getName();
85 //函数表达式名字为空用id删除
8626 if(name) {
8712 if(name in this.vardeclMap) {
881 return this;
89 }
9011 this.delVar(name);
9111 this.delChild(name);
92 }
93 else {
9414 this.delChild(child.getId());
95 }
9625 name = name || child.getId();
9725 this.childrenMap[name] = child;
9825 this.children.push(child);
9925 return this;
100 },
101 //name函数声明,id表达式
102 delChild: function(name) {
10338 if(this.hasChild(name)) {
1041 var i = this.children.indexOf(this.childrenMap[name]);
1051 this.children.splice(i, 1);
1061 delete this.childrenMap[name];
107 }
10838 return this;
109 },
110 hasVar: function(v) {
11138 return v in this.varsMap;
112 },
113 addVar: function(node, assign) {
11417 var v = node.leaves()[0].token().content();
115 //赋值拥有最高优先级,会覆盖掉之前的函数声明和var
11617 if(assign) {
11713 this.delVar(v);
11813 this.delChild(v);
11913 this.vardeclMap[v] = true;
120 }
121 //仅仅是var声明无赋值,且已有过声明或函数,忽略之
1224 else if(this.hasVar(v) || this.hasChild(v)) {
1232 return this;
124 }
12515 this.varsMap[v] = node;
12615 this.vars.push(node);
12715 return this;
128 },
129 delVar: function(v) {
13024 if(this.hasVar(v)) {
1312 var i = this.vars.indexOf(this.varsMap[v]);
1322 this.vars.splice(i, 1);
1332 delete this.varsMap[v];
134 }
13524 return this;
136 },
137 getVars: function() {
1384 return this.vars;
139 },
140 addReturn: function(node) {
1415 this.returns.push(node);
1425 return this;
143 },
144 getReturns: function() {
1454 return this.returns;
146 },
147 hasVid: function(v) {
1488 return v in this.vidsMap;
149 },
150 getVid: function(v) {
1511 return this.vidsMap[v];
152 },
153 addVid: function(node) {
15418 var v = node.token().content();
15518 this.vids.push(node);
15618 this.vidsMap[v] = this.vidsMap[v] || [];
15718 this.vidsMap[v].push(node);
15818 return this;
159 },
160 getVids: function() {
1612 return this.vids;
162 },
163 getNode: function() {
1641 return this.node;
165 },
166 setNode: function(n) {
16726 this.node = n;
16826 return this;
169 },
170 setThis: function(t) {
1714 this.thisIs = t;
1724 return this;
173 },
174 getThis: function() {
1753 return this.thisIs;
176 }
177});
178
1791function recursion(node, context) {
180662 var isToken = node.name() == JsNode.TOKEN;
181662 var isVirtual = isToken && node.token().type() == Token.VIRTUAL;
182662 if(isToken) {
183382 if(!isVirtual) {
184376 var token = node.token();
185376 var s = token.content();
186376 if(s == 'return') {
1875 context.addReturn(node);
188 }
189 }
190 }
191 else {
192280 if(node.name() == JsNode.VARDECL) {
19317 vardecl(node, context);
194 }
195263 else if(node.name() == JsNode.FNDECL) {
19612 context = fndecl(node, context);
197 }
198251 else if(node.name() == JsNode.FNEXPR) {
19914 context = fnexpr(node, context);
200 }
201237 else if(node.name() == JsNode.PRMREXPR) {
20251 prmrexpr(node, context);
203 }
204280 node.leaves().forEach(function(leaf, i) {
205627 recursion(leaf, context);
206 });
207 }
208}
2091function vardecl(node, context) {
21017 var leaves = node.leaves();
21117 var assign = !!leaves[1];
21217 context.addVar(node, assign);
213}
2141function fndecl(node, context) {
21512 var v = node.leaves()[1].leaves().content();
21612 var child = new Context(context, v);
21712 child.setNode(node);
21812 var params = node.leaves()[3];
21912 if(params.name() == JsNode.FNPARAMS) {
2201 addParam(params, child);
221 }
22212 return child;
223}
2241function fnexpr(node, context) {
225 //函数表达式name为空
22614 var child = new Context(context);
22714 child.setNode(node);
228 //记录形参
22914 var params;
23014 var v = node.leaves()[1];
23114 if(v.token().content() != '(') {
2326 params = node.leaves()[3];
233 }
234 else {
2358 params = node.leaves()[2];
236 }
23714 if(params.name() == JsNode.FNPARAMS) {
2387 addParam(params, child);
239 }
240 //匿名函数检查实参传入情况,包括call和apply设置this
24114 var next = node.next();
242 //!function(){}()形式
24314 if(next && next.name() == JsNode.ARGS) {
2447 var leaves = next.leaves();
245 //长度2为()空参数,长度3有参数,第2个节点
2467 if(leaves.length == 3) {
2471 addAParam(leaves[1], child);
248 }
249 }
250 //call或applay
2517 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) {
2572 var mmb = node.parent();
2582 if(mmb.name() == JsNode.MMBEXPR) {
2592 var callexpr = mmb.parent();
2602 if(callexpr.name() == JsNode.CALLEXPR) {
2612 var isApply = next.next().token().content() == 'apply';
2622 next = mmb.next();
2632 if(next && next.name() == JsNode.ARGS) {
2642 var leaves = next.leaves();
265 //长度2为()空参数,长度3有参数,第2个节点
2662 if(leaves.length == 3) {
2672 isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child);
268 }
269 }
270 }
271 }
272 }
273 //(function(){})()形式
274 else {
2755 var prmr = node.parent();
2765 var prev = node.prev();
2775 if(prmr.name() == JsNode.PRMREXPR
278 && prev
279 && prev.name() == JsNode.TOKEN
280 && prev.token().content() == '(') {
2815 next = prmr.next();
2825 if(next && next.name() == JsNode.ARGS) {
2833 var leaves = next.leaves();
284 //长度2为()空参数,长度3有参数,第2个节点
2853 if(leaves.length == 3) {
2861 addAParam(leaves[1], child);
287 }
288 }
289 //(function(){}).call()形式
2902 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) {
2962 var mmb = prmr.parent();
2972 if(mmb.name() == JsNode.MMBEXPR) {
2982 var callexpr = mmb.parent();
2992 if(callexpr.name() == JsNode.CALLEXPR) {
3002 var isApply = next.next().token().content() == 'apply';
3012 next = mmb.next();
3022 if(next && next.name() == JsNode.ARGS) {
3032 var leaves = next.leaves();
304 //长度2为()空参数,长度3有参数,第2个节点
3052 if(leaves.length == 3) {
3062 isApply ? addApplyAParam(leaves[1], child) : addCallAParam(leaves[1], child);
307 }
308 else {
3090 child.setThis(undefined);
310 }
311 }
312 }
313 }
314 }
315 }
316 }
31714 return child;
318}
319
3201function addParam(params, child) {
3218 params.leaves().forEach(function(leaf, i) {
32212 if(leaf.name() == JsNode.TOKEN && leaf.token().content() != ',') {
32310 child.addParam(leaf.token().content());
324 }
325 });
326}
3271function addAParam(params, child) {
3282 params.leaves().forEach(function(leaf, i) {
3296 if(i % 2 == 0) {
3304 child.addAParam(leaf);
331 }
332 });
333}
3341function addCallAParam(params, child) {
3352 params.leaves().forEach(function(leaf, i) {
3366 if(i == 0) {
3372 child.setThis(leaf);
338 }
3394 else if(i % 2 == 1) {
3402 child.addAParam(leaf);
341 }
342 });
343}
3441function addApplyAParam(params, child) {
3452 child.setThis(params.leaves()[0]);
3462 if(params.leaves()[2]) {
3472 params.leaves()[2].leaves()[0].leaves().forEach(function(leaf, i) {
3488 if(i % 2 == 1) {
3493 child.addAParam(leaf);
350 }
351 });
352 }
353}
3541function prmrexpr(node, context) {
35551 var first = node.leaves()[0];
35651 if(first.name() == JsNode.TOKEN) {
35747 var token = first.token();
35847 if(token.type() == Token.ID || token.content() == 'this') {
35918 context.addVid(first);
360 }
361 }
362}
363
3641module.exports = Context;

/Users/army/Sites/homunculus/src/parser/js/Node.js

97%
47
46
1
LineHitsSource
11var Class = require('../../util/Class');
21var Node = Class(function(type, children) {
3770065 this.type = type;
4770065 if(type == Node.TOKEN) {
5140032 this.children = children;
6 }
7630033 else if(Array.isArray(children)) {
80 this.children = children;
9 }
10 else {
11630033 this.children = children ? [children] : [];
12 }
13770065 this.p = null;
14770065 this.pr = null;
15770065 this.ne = null;
16770065 return this;
17}).methods({
18 name: function() {
19240238 return this.type;
20 },
21 leaves: function() {
2298819 return this.children;
23 },
24 leaf: function(i) {
251 return this.children[i];
26 },
27 number: function() {
281 return this.children.length;
29 },
30 add: function() {
31169467 var self = this;
32169467 var args = Array.prototype.slice.call(arguments, 0);
33169467 args.forEach(function(node) {
34238519 node.parent(self);
35238519 var last = self.children[self.children.length - 1];
36238519 if(last) {
37139887 last.next(node);
38139887 node.prev(last);
39 }
40238519 self.children.push(node);
41 });
42169467 return self;
43 },
44 token: function() {
45278406 return this.children;
46 },
47 parent: function(p) {
48238535 if(p) {
49238519 this.p = p;
50 }
51238535 return this.p;
52 },
53 prev: function(pr) {
54139894 if(pr) {
55139887 this.pr = pr;
56 }
57139894 return this.pr;
58 },
59 next: function(ne) {
60139927 if(ne) {
61139887 this.ne = ne;
62 }
63139927 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 BINDREST: 'bindrest',
143 BINDID: 'bindid',
144 SPREAD: 'spread',
145 ARRCMPH: 'arrcmph',
146 CMPHFOR: 'cmphfor',
147 getKey: function(s) {
1482 if(!s) {
1491 throw new Error('empty value');
150 }
1511 if(!keys) {
1521 var self = this;
1531 keys = {};
1541 Object.keys(this).forEach(function(k) {
15584 var v = self[k];
15684 keys[v] = k;
157 });
158 }
1591 return keys[s];
160 }
161});
1621var keys;
1631module.exports = Node;

/Users/army/Sites/homunculus/src/parser/js/Parser.js

99%
533
529
4
LineHitsSource
11var Class = require('../../util/Class');
21var character = require('../../util/character');
31var Lexer = require('../../lexer/Lexer');
41var Rule = require('../../lexer/rule/EcmascriptRule');
51var Token = require('../../lexer/Token');
61var Node = require('./Node');
71var S = {};
81S[Token.BLANK] = S[Token.TAB] = S[Token.COMMENT] = S[Token.LINE] = S[Token.ENTER] = true;
91var Parser = Class(function(lexer) {
10202 this.init(lexer);
11}).methods({
12 parse: function(code) {
13167 this.lexer.parse(code);
14167 this.tree = this.program();
15141 return this.tree;
16 },
17 ast: function() {
184 return this.tree;
19 },
20 init: function(lexer) {
21203 this.look = null;
22203 this.tokens = null;
23203 this.lastLine = 1;
24203 this.lastCol = 1;
25203 this.line = 1;
26203 this.col = 1;
27203 this.index = 0;
28203 this.length = 0;
29203 this.ignores = {};
30203 this.hasMoveLine = false;
31203 this.tree = {};
32203 if(lexer) {
33137 this.lexer = lexer;
34 }
3566 else if(this.lexer) {
361 this.lexer.init();
37 }
38 else {
3965 this.lexer = new Lexer(new Rule());
40 }
41 },
42 program: function() {
43167 this.tokens = this.lexer.tokens();
44167 this.length = this.tokens.length;
45167 if(this.tokens.length) {
46165 this.move();
47 }
48167 var node = new Node(Node.PROGRAM);
49167 while(this.look) {
50198 node.add(this.element());
51 }
52141 return node;
53 },
54 element: function(allowSuper) {
555364 if(this.look.content() == 'function') {
56198 return this.fndecl();
57 }
58 else {
595166 return this.stmt(allowSuper);
60 }
61 },
62 stmt: function(allowSuper) {
6310852 if(!this.look) {
641 this.error();
65 }
6610851 switch(this.look.content()) {
67 case 'var':
681163 return this.varstmt();
69 case '{':
701791 return this.block();
71 case ';':
7235 return this.emptstmt();
73 case 'if':
741692 return this.ifstmt();
75 case 'do':
76 case 'while':
77 case 'for':
78458 return this.iterstmt();
79 case 'continue':
8018 return this.cntnstmt();
81 case 'break':
82123 return this.brkstmt();
83 case 'return':
841636 return this.retstmt();
85 case 'with':
861 return this.withstmt();
87 case 'switch':
888 return this.swchstmt();
89 case 'throw':
9039 return this.thrstmt();
91 case 'try':
9255 return this.trystmt();
93 case 'debugger':
941 return this.debstmt();
95 default:
963831 if(this.look.type() == Token.ID) {
972943 for(var i = this.index; i < this.length; i++) {
983955 var token = this.tokens[i];
993955 if(!S[token.type()]) {
1002940 if(token.content() == ':') {
1012 return this.labstmt();
102 }
103 else {
1042938 return this.exprstmt();
105 }
106 }
107 }
108 }
109891 return this.exprstmt();
110 }
111 },
112 exprstmt: function() {
1133829 var node = new Node(Node.EXPRSTMT);
1143829 node.add(this.expr(), this.match(';'));
1153819 return node;
116 },
117 varstmt: function(noSem) {
1181247 var node = new Node(Node.VARSTMT);
1191247 node.add(
120 this.match('var'),
121 this.vardecl()
122 );
1231244 while(this.look && this.look.content() == ',') {
1241555 node.add(
125 this.match(),
126 this.vardecl()
127 );
128 }
1291244 if(!noSem) {
1301160 node.add(this.match(';'));
131 }
1321244 return node;
133 },
134 vardecl: function() {
1352802 var node = new Node(Node.VARDECL);
1362802 if(!this.look) {
1371 this.error('missing variable name');
138 }
1392801 node.add(this.match(Token.ID, 'missing variable name'));
1402801 if(this.look && this.look.content() == '=') {
1411919 node.add(this.assign());
142 }
1432799 return node;
144 },
145 assign: function() {
1461919 var node = new Node(Node.ASSIGN);
1471919 node.add(this.match('='));
1481919 if(!this.look) {
1491 this.error();
150 }
1511918 node.add(this.assignexpr());
1521917 return node;
153 },
154 block: function() {
1551906 var node = new Node(Node.BLOCK);
1561906 node.add(this.match('{'));
1571906 while(this.look && this.look.content() != '}') {
1582988 node.add(this.stmt());
159 }
1601906 node.add(this.match('}', 'missing } in compound statement'));
1611906 return node;
162 },
163 emptstmt: function() {
16435 var node = new Node(Node.EMPTSTMT);
16535 node.add(this.match(';'));
16635 return node;
167 },
168 ifstmt: function() {
1691692 var node = new Node(Node.IFSTMT);
1701692 node.add(
171 this.match('if'),
172 this.match('('),
173 this.expr(),
174 this.match(')'),
175 this.stmt()
176 );
1771691 if(this.look && this.look.content() == 'else') {
178325 node.add(
179 this.match('else'),
180 this.stmt()
181 );
182 }
1831691 return node;
184 },
185 iterstmt: function() {
186458 var node = new Node(Node.ITERSTMT);
187458 switch(this.look.content()) {
188 case 'do':
1899 node.add(
190 this.match(),
191 this.stmt(),
192 this.match('while'),
193 this.match('('),
194 this.expr(),
195 this.match(')'),
196 this.match(';')
197 );
1989 break;
199 case 'while':
200152 node.add(
201 this.match(),
202 this.match('('),
203 this.expr(),
204 this.match(')'),
205 this.stmt()
206 );
207152 break;
208 case 'for':
209297 node.add(
210 this.match(),
211 this.match('(')
212 );
213297 if(!this.look) {
2141 this.error();
215 }
216296 if(this.look.content() == 'var' || this.look.content() == 'let') {
21784 var node2 = this.look.content() == 'var' ? this.varstmt(true) : this.letstmt(true);
21884 if(!this.look) {
2191 this.error('missing ; after for-loop initializer');
220 }
22183 if(this.look.content() == 'in') {
22219 if(node2.leaves().length > 2) {
2231 this.error('invalid for/in left-hand side');
224 }
22518 node.add(node2);
22618 node.add(
227 this.match(),
228 this.expr()
229 );
230 }
231 else {
23264 node.add(node2);
23364 node.add(this.match(';'));
23464 if(this.look.content() != ';') {
23563 node.add(this.expr());
236 }
23763 node.add(this.match(';'));
23863 if(!this.look) {
2391 this.error();
240 }
24162 if(this.look.content() != ')') {
24259 node.add(this.expr());
243 }
244 }
245 }
246 else {
247212 if(this.look.content() == 'in') {
2481 this.error();
249 }
250211 var hasIn = false;
251211 for(var i = this.index; i < this.length; i++) {
2521924 var t = this.tokens[i];
2531924 if(t.content() == 'in') {
25487 hasIn = true;
25587 break;
256 }
2571837 else if(t.content() == ')') {
258120 break;
259 }
260 }
261211 if(hasIn) {
26287 node.add(this.expr(true), this.match('in'), this.expr());
263 }
264 else {
265124 if(this.look.content() != ';') {
26668 node.add(this.expr());
267 }
268 //for的;不能省略,强制判断
269124 if(!this.look || this.look.content() != ';') {
2701 this.error('missing ;')
271 }
272123 node.add(this.match(';'));
273123 if(!this.look) {
2741 this.error();
275 }
276122 if(this.look.content() != ';') {
277121 node.add(this.expr());
278 }
279122 if(!this.look || this.look.content() != ';') {
2801 this.error('missing ;')
281 }
282121 node.add(this.match(';'));
283121 if(!this.look) {
2841 this.error();
285 }
286120 if(this.look.content() != ')') {
287112 node.add(this.expr());
288 }
289 }
290 }
291287 node.add(this.match(')'));
292287 node.add(this.stmt());
293 }
294448 return node;
295 },
296 cntnstmt: function() {
29718 var node = new Node(Node.CNTNSTMT);
29818 node.add(this.match('continue', true));
29918 if(this.look && this.look.type() == Token.ID) {
3001 node.add(this.match());
301 }
30218 node.add(this.match(';'));
30318 return node;
304 },
305 brkstmt: function() {
306123 var node = new Node(Node.BRKSTMT);
307123 node.add(this.match('break', true));
308123 if(this.look && this.look.type() == Token.ID) {
3091 node.add(this.match());
310 }
311123 node.add(this.match(';'));
312123 return node;
313 },
314 retstmt: function() {
3151636 var node = new Node(Node.RETSTMT);
3161636 node.add(this.match('return', true));
317 //return后换行视作省略;,包括多行注释的换行
3181636 if(this.look) {
3191635 if(this.look.content() == ';'
320 || this.look.content() == '}'
321 || this.look.type() == Token.LINE
322 || this.look.type() == Token.COMMENT) {
32382 node.add(this.match(';'));
324 }
325 else {
3261553 node.add(this.expr(), this.match(';'));
327 }
328 }
329 else {
3301 node.add(this.match(';'));
331 }
3321636 return node;
333 },
334 withstmt: function() {
3351 var node = new Node(Node.WITHSTMT);
3361 node.add(
337 this.match('with'),
338 this.match('('),
339 this.expr(),
340 this.match(')'),
341 this.stmt()
342 );
3431 return node;
344 },
345 swchstmt: function() {
3468 var node = new Node(Node.SWCHSTMT);
3478 node.add(
348 this.match('switch'),
349 this.match('('),
350 this.expr(),
351 this.match(')'),
352 this.caseblock()
353 );
3547 return node;
355 },
356 caseblock: function() {
3578 var node = new Node(Node.CASEBLOCK);
3588 node.add(this.match('{'));
3598 while(this.look && this.look.content() != '}') {
360102 if(this.look.content() == 'case') {
36199 node.add(this.caseclause());
362 }
3633 else if(this.look.content() == 'default') {
3642 node.add(this.dftclause());
365 }
366 else {
3671 this.error('invalid switch statement');
368 }
369 }
3707 node.add(this.match('}'));
3717 return node;
372 },
373 caseclause: function() {
37499 var node = new Node(Node.CASECLAUSE);
37599 node.add(
376 this.match('case'),
377 this.expr(),
378 this.match(':')
379 );
38099 while(this.look
381 && this.look.content() != 'case'
382 && this.look.content() != 'default'
383 && this.look.content() != '}') {
384228 node.add(this.stmt());
385 }
38699 return node;
387 },
388 dftclause: function() {
3892 var node = new Node(Node.DFTCLAUSE);
3902 node.add(
391 this.match('default'),
392 this.match(':')
393 );
3942 while(this.look && this.look.content() != '}') {
3952 node.add(this.stmt());
396 }
3972 return node;
398 },
399 labstmt: function() {
4002 var node = new Node(Node.LABSTMT);
4012 node.add(
402 this.match(Token.ID),
403 this.match(':'),
404 this.stmt()
405 );
4062 return node;
407 },
408 thrstmt: function() {
40939 var node = new Node(Node.THRSTMT);
41039 node.add(
411 this.match('throw', true),
412 this.expr(),
413 this.match(';')
414 );
41539 return node;
416 },
417 trystmt: function() {
41855 var node = new Node(Node.TRYSTMT);
41955 node.add(
420 this.match('try'),
421 this.block()
422 );
42355 if(this.look && this.look.content() == 'catch') {
42454 node.add(this.cach());
42554 if(this.look && this.look.content() == 'finally') {
4265 node.add(this.finl());
427 }
428 }
429 else {
4301 node.add(this.finl());
431 }
43255 return node;
433 },
434 debstmt: function() {
4351 var node = new Node(Node.DEBSTMT);
4361 node.add(this.match('debugger'), this.match(';'));
4371 return node;
438 },
439 cach: function() {
44054 var node = new Node(Node.CACH);
44154 node.add(
442 this.match('catch'),
443 this.match('('),
444 this.match(Token.ID, 'missing identifier in catch'),
445 this.match(')'),
446 this.block()
447 );
44854 return node;
449 },
450 finl: function() {
4516 var node = new Node(Node.FINL);
4526 node.add(
453 this.match('finally'),
454 this.block()
455 );
4566 return node;
457 },
458 fndecl: function() {
459198 var node = new Node(Node.FNDECL);
460198 node.add(
461 this.match('function'),
462 this.match(Token.ID, 'function statement requires a name'),
463 this.match('(')
464 );
465198 if(!this.look) {
4661 this.error('missing formal parameter');
467 }
468197 if(this.look.content() != ')') {
469154 node.add(this.fnparams());
470 }
471197 node.add(
472 this.match(')'),
473 this.match('{'),
474 this.fnbody(),
475 this.match('}')
476 );
477197 return node;
478 },
479 fnexpr: function() {
4801625 var node = new Node(Node.FNEXPR);
4811625 node.add(this.match('function'));
4821625 if(!this.look) {
4831 this.error('missing formal parameter');
484 }
4851624 if(this.look.type() == Token.ID) {
48622 node.add(this.match());
487 }
4881624 node.add(this.match('('));
4891624 if(!this.look) {
4901 this.error();
491 }
4921623 if(this.look.content() != ')') {
4931147 node.add(this.fnparams());
494 }
4951622 node.add(
496 this.match(')'),
497 this.match('{'),
498 this.fnbody(),
499 this.match('}', 'missing } in compound statement')
500 );
5011621 return node;
502 },
503 fnparams: function() {
5041301 var node = new Node(Node.FNPARAMS);
5051301 while(this.look && this.look.content() != ')') {
5062282 node.add(this.match(Token.ID, 'missing formal parameter'));
5072281 if(this.look && this.look.content() == ',') {
508982 node.add(this.match());
509 }
510 }
5111300 return node;
512 },
513 fnbody: function(allowSuper) {
5141818 var node = new Node(Node.FNBODY);
5151818 while(this.look && this.look.content() != '}') {
5165166 node.add(this.element(allowSuper));
517 }
5181818 return node;
519 },
520 expr: function(noIn) {
52111058 var node = new Node(Node.EXPR),
522 assignexpr = this.assignexpr(noIn);
52311044 if(this.look && this.look.content() == ',') {
524540 node.add(assignexpr);
525540 while(this.look && this.look.content() == ',') {
5261051 node.add(this.match(), this.assignexpr(noIn));
527 }
528 }
529 else {
53010504 return assignexpr;
531 }
532540 return node;
533 },
534 assignexpr: function(noIn) {
53530764 var node = new Node(Node.ASSIGNEXPR),
536 cndt = this.cndtexpr(noIn);
53730749 if(this.look && {
538 '*=': true,
539 '/=': true,
540 '%=': true,
541 '+=': true,
542 '-=': true,
543 '<<=': true,
544 '>>=': true,
545 '>>>=': true,
546 '&=': true,
547 '^=': true,
548 '|=': true,
549 '=': true
550 }.hasOwnProperty(this.look.content())) {
5513675 node.add(cndt, this.match(), this.assignexpr(noIn));
552 }
553 else {
55427074 return cndt;
555 }
5563675 return node;
557 },
558 cndtexpr: function(noIn) {
55930764 var node = new Node(Node.CNDTEXPR),
560 logorexpr = this.logorexpr(noIn);
56130749 if(this.look && this.look.content() == '?') {
562878 node.add(
563 logorexpr,
564 this.match(),
565 this.assignexpr(noIn),
566 this.match(':'),
567 this.assignexpr(noIn)
568 );
569 }
570 else {
57129871 return logorexpr;
572 }
573878 return node;
574 },
575 logorexpr: function(noIn) {
57630764 var node = new Node(Node.LOGOREXPR),
577 logandexpr = this.logandexpr(noIn);
57830749 if(this.look && this.look.content() == '||') {
5791015 node.add(logandexpr);
5801015 while(this.look && this.look.content() == '||') {
5811150 node.add(
582 this.match(),
583 this.logandexpr(noIn)
584 );
585 }
586 }
587 else {
58829734 return logandexpr;
589 }
5901015 return node;
591 },
592 logandexpr: function(noIn) {
59331914 var node = new Node(Node.LOGANDEXPR),
594 bitorexpr = this.bitorexpr(noIn);
59531899 if(this.look && this.look.content() == '&&') {
5961129 node.add(bitorexpr);
5971129 while(this.look && this.look.content() == '&&') {
5981375 node.add(
599 this.match(),
600 this.bitorexpr(noIn)
601 );
602 }
603 }
604 else {
60530770 return bitorexpr;
606 }
6071129 return node;
608 },
609 bitorexpr: function(noIn) {
61033289 var node = new Node(Node.BITOREXPR),
611 bitxorexpr = this.bitxorexpr(noIn);
61233274 if(this.look && this.look.content() == '|') {
6135 node.add(bitxorexpr);
6145 while(this.look && this.look.content() == '|') {
6155 node.add(
616 this.match(),
617 this.bitxorexpr(noIn)
618 );
619 }
620 }
621 else {
62233269 return bitxorexpr;
623 }
6245 return node;
625 },
626 bitxorexpr: function(noIn) {
62733294 var node = new Node(Node.BITXOREXPR),
628 bitandexpr = this.bitandexpr(noIn);
62933279 if(this.look && this.look.content() == '^') {
6302 node.add(bitandexpr);
6312 while(this.look && this.look.content() == '^') {
6322 node.add(
633 this.match(),
634 this.bitandexpr(noIn)
635 );
636 }
637 }
638 else {
63933277 return bitandexpr;
640 }
6412 return node;
642 },
643 bitandexpr: function(noIn) {
64433296 var node = new Node(Node.BITANDEXPR),
645 eqexpr = this.eqexpr(noIn);
64633281 if(this.look && this.look.content() == '&') {
64718 node.add(eqexpr);
64818 while(this.look && this.look.content() == '&') {
64918 node.add(
650 this.match(),
651 this.eqexpr(noIn)
652 );
653 }
654 }
655 else {
65633263 return eqexpr;
657 }
65818 return node;
659 },
660 eqexpr: function(noIn) {
66133314 var node = new Node(Node.EQEXPR),
662 reltexpr = this.reltexpr(noIn);
66333299 if(this.look && {
664 '==': true,
665 '===': true,
666 '!==': true,
667 '!=': true
668 }.hasOwnProperty(this.look.content())) {
6691589 node.add(reltexpr);
6701589 while(this.look && {
671 '==': true,
672 '===': true,
673 '!==': true,
674 '!=': true
675 }.hasOwnProperty(this.look.content())) {
6761590 node.add(
677 this.match(),
678 this.reltexpr(noIn)
679 );
680 }
681 }
682 else {
68331710 return reltexpr;
684 }
6851589 return node;
686 },
687 reltexpr: function(noIn) {
68834904 var node = new Node(Node.RELTEXPR),
689 shiftexpr = this.shiftexpr();
69034889 if(this.look && ({
691 '<': true,
692 '>': true,
693 '>=': true,
694 '<=': true,
695 'instanceof': true
696 }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
697477 node.add(shiftexpr);
698477 while(this.look && ({
699 '<': true,
700 '>': true,
701 '>=': true,
702 '<=': true,
703 'instanceof': true
704 }.hasOwnProperty(this.look.content()) || (!noIn && this.look.content() == 'in'))) {
705477 node.add(
706 this.match(),
707 this.shiftexpr()
708 );
709 }
710 }
711 else {
71234412 return shiftexpr;
713 }
714477 return node;
715 },
716 shiftexpr: function() {
71735381 var node = new Node(Node.SHIFTEXPR),
718 addexpr = this.addexpr();
71935366 if(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
7208 node.add(addexpr);
7218 while(this.look && ['<<', '>>', '>>>'].indexOf(this.look.content()) != -1) {
7229 node.add(
723 this.match(),
724 this.addexpr()
725 );
726 }
727 }
728 else {
72935358 return addexpr;
730 }
7318 return node;
732 },
733 addexpr: function() {
73435390 var node = new Node(Node.ADDEXPR),
735 mtplexpr = this.mtplexpr();
73635375 if(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
737777 node.add(mtplexpr);
738777 while(this.look && ['+', '-'].indexOf(this.look.content()) != -1) {
7391318 node.add(
740 this.match(),
741 this.mtplexpr()
742 );
743 }
744 }
745 else {
74634598 return mtplexpr;
747 }
748777 return node;
749 },
750 mtplexpr: function() {
75136708 var node = new Node(Node.MTPLEXPR),
752 unaryexpr = this.unaryexpr();
75336693 if(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
75451 node.add(unaryexpr);
75551 while(this.look && ['*', '/', '%'].indexOf(this.look.content()) != -1) {
75652 node.add(
757 this.match(),
758 this.unaryexpr()
759 );
760 }
761 }
762 else {
76336642 return unaryexpr;
764 }
76551 return node;
766 },
767 unaryexpr: function() {
76838478 var node = new Node(Node.UNARYEXPR);
76938478 if(!this.look) {
7702 this.error();
771 }
77238476 switch(this.look.content()) {
773 case 'delete':
774 case 'void':
775 case 'typeof':
776 case '++':
777 case '--':
778 case '+':
779 case '-':
780 case '~':
781 case '!':
7821718 node.add(
783 this.match(),
784 this.unaryexpr()
785 );
7861716 break;
787 default:
78836758 return this.postfixexpr();
789 }
7901716 return node;
791 },
792 postfixexpr: function() {
79336758 var node = new Node(Node.POSTFIXEXPR);
79436758 var leftexpr = this.leftexpr();
79536745 if(this.look && ['++', '--'].indexOf(this.look.content()) > -1 && !this.hasMoveLine) {
796327 node.add(leftexpr);
797327 while(this.look && ['++', '--'].indexOf(this.look.content()) > -1) {
798327 node.add(this.match(undefined, true));
799 }
800 }
801 else {
80236418 return leftexpr;
803 }
804327 return node;
805 },
806 leftexpr: function() {
80736758 if(this.look.content() == 'new') {
808190 return this.newexpr();
809 }
810 else {
81136568 return this.callexpr();
812 }
813 },
814 newexpr: function(depth) {
815191 depth = depth || 0;
816191 var node = new Node(Node.NEWEXPR);
817191 node.add(this.match('new'));
818191 if(!this.look) {
8191 this.error();
820 }
821190 if(this.look.content() == 'new') {
8221 node.add(this.newexpr(depth + 1));
823 }
824 else {
825189 node.add(this.mmbexpr());
826 }
827190 if(this.look && this.look.content() == '(') {
828179 node.add(this.args());
829 }
830190 if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
83111 var mmb = new Node(Node.MMBEXPR);
83211 mmb.add(node);
83311 while(this.look) {
83420 if(this.look.content() == '.') {
83510 mmb.add(
836 this.match(),
837 this.match(Token.ID)
838 );
839 }
84010 else if(this.look.content() == '[') {
8411 mmb.add(
842 this.match(),
843 this.expr(),
844 this.match(']')
845 );
846 }
847 else {
8489 break;
849 }
850 }
85111 if(depth == 0 && this.look && this.look.content() == '(') {
8528 var callexpr = this.callexpr(mmb);
8538 return callexpr;
854 }
8553 return mmb;
856 }
857179 return node;
858 },
859 callexpr: function(mmb) {
86036576 var node = new Node(Node.CALLEXPR);
86136576 mmb = mmb || this.mmbexpr();
86236565 if(this.look && this.look.content() == '(') {
8635156 node.add(
864 mmb,
865 this.args()
866 );
8675155 if(this.look && ['.', '[', '('].indexOf(this.look.content()) > -1) {
868364 while(this.look) {
8691186 if(this.look.content() == '.') {
870426 node.add(
871 this.match(),
872 this.match(Token.ID)
873 );
874 }
875760 else if(this.look.content() == '[') {
87645 node.add(
877 this.match(),
878 this.expr(),
879 this.match(']')
880 );
881 }
882715 else if(this.look.content() == '(') {
883351 node.add(this.args());
884 }
885 else {
886364 break;
887 }
888 }
889 }
890 }
891 else {
89231409 return mmb;
893 }
8945155 return node;
895 },
896 mmbexpr: function() {
89736757 var node = new Node(Node.MMBEXPR);
89836757 var mmb;
89936757 if(this.look.content() == 'function') {
9001625 mmb = this.fnexpr();
901 }
902 else {
90335132 mmb = this.prmrexpr();
904 }
90536746 if(this.look && ['.', '['].indexOf(this.look.content()) > -1) {
90610737 node.add(mmb);
90710737 while(this.look) {
90823519 if(this.look.content() == '.') {
90910859 node.add(
910 this.match(),
911 this.match(Token.ID)
912 );
913 }
91412660 else if(this.look.content() == '[') {
9151928 node.add(
916 this.match(),
917 this.expr(),
918 this.match(']')
919 );
920 }
921 else {
92210732 break;
923 }
924 }
925 }
926 else {
92726009 return mmb;
928 }
92910737 return node;
930 },
931 prmrexpr: function() {
93235132 var node = new Node(Node.PRMREXPR);
93335132 switch(this.look.type()) {
934 case Token.ID:
935 case Token.NUMBER:
936 case Token.STRING:
937 case Token.REG:
938 case Token.TEMPLATE:
93929061 node.add(this.match());
94029061 break;
941 default:
9426071 switch(this.look.content()) {
943 case 'this':
944 case 'null':
945 case 'true':
946 case 'false':
9473381 node.add(this.match());
9483381 break;
949 case '(':
9501087 node.add(this.match(), this.expr(), this.match(')'));
9511083 break;
952 case '[':
953870 node.add(this.arrltr());
954869 break;
955 case '{':
956732 node.add(this.objltr());
957731 break;
958 default:
9591 this.error();
960 }
961 }
96235125 return node;
963 },
964 arrltr: function() {
965870 var node = new Node(Node.ARRLTR);
966870 node.add(this.match('['));
967870 while(this.look && this.look.content() != ']') {
9682380 if(this.look.content() == ',') {
969874 node.add(this.match());
970 }
971 else {
9721506 node.add(this.assignexpr());
973 }
974 }
975870 node.add(this.match(']', 'missing ] after element list'));
976869 return node;
977 },
978 objltr: function() {
979732 var node = new Node(Node.OBJLTR);
980732 node.add(this.match('{'));
981732 while(this.look && this.look.content() != '}') {
9822006 node.add(this.proptassign());
9832005 if(this.look && this.look.content() == ',') {
9841513 node.add(this.match());
985 }
986 }
987731 node.add(this.match('}', 'missing } after property list'));
988731 return node;
989 },
990 proptassign: function() {
9912006 var node = new Node(Node.PROPTASSIGN);
9922006 switch(this.look.type()) {
993 case Token.ID:
994 case Token.STRING:
995 case Token.NUMBER:
9962005 node.add(
997 this.match(),
998 this.match(':', 'missing : after property id'),
999 this.assignexpr()
1000 );
10012005 break;
1002 default:
10031 this.error('invalid property id');
1004 }
10052005 return node;
1006 },
1007 args: function() {
10085686 var node = new Node(Node.ARGS);
10095686 node.add(this.match('('));
10105686 if(!this.look) {
10111 this.error();
1012 }
10135685 if(this.look.content() != ')') {
10144947 node.add(this.arglist());
1015 }
10165685 node.add(this.match(')'));
10175685 return node;
1018 },
1019 arglist: function() {
10204947 var node = new Node(Node.ARGLIST);
10214947 while(this.look && this.look.content() != ')') {
10227795 node.add(this.assignexpr());
10237795 if(this.look && this.look.content() == ',') {
10242848 node.add(this.match());
1025 }
1026 }
10274947 return node;
1028 },
1029 virtual: function(s) {
10301578 return new Node(Node.TOKEN, new Token(Token.VIRTUAL, s));
1031 },
1032 match: function(type, line, msg) {
1033140035 if(typeof type == 'boolean') {
10340 msg = line;
10350 line = type;
10360 type = undefined;
1037 }
1038140035 if(typeof line != 'boolean') {
1039137892 line = false;
1040137892 msg = line;
1041 }
1042 //未定义为所有非空白token
1043140035 if(character.isUndefined(type)) {
104470720 if(this.look) {
104570720 var l = this.look;
104670720 this.move(line);
104770720 return new Node(Node.TOKEN, l);
1048 }
1049 else {
10500 this.error('syntax error' + (msg || ''));
1051 }
1052 }
1053 //或者根据token的type或者content匹配
105469315 else if(typeof type == 'string') {
1055 //特殊处理;,不匹配但有换行或者末尾时自动补全,还有受限行
105652683 if(type == ';'
1057 && (!this.look
1058 || (this.look.content() != type && this.hasMoveLine)
1059 || this.look.content() == '}')
1060 ) {
10611578 if(this.look && S[this.look.type()]) {
106236 this.move();
1063 }
10641578 return this.virtual(';');
1065 }
106651105 else if(this.look && this.look.content() == type) {
106751103 var l = this.look;
106851103 this.move(line);
106951103 return new Node(Node.TOKEN, l);
1070 }
1071 else {
10722 this.error('missing ' + type + (msg || ''));
1073 }
1074 }
107516632 else if(typeof type == 'number') {
107616632 if(this.look && this.look.type() == type) {
107716631 var l = this.look;
107816631 this.move(line);
107916631 return new Node(Node.TOKEN, l);
1080 }
1081 else {
10821 this.error('missing ' + Token.type(type) + (msg || ''));
1083 }
1084 }
1085 },
1086 move: function(line) {
1087138655 this.lastLine = this.line;
1088138655 this.lastCol = this.col;
1089 //遗留下来的换行符
1090138655 this.hasMoveLine = false;
1091138655 do {
1092247829 this.look = this.tokens[this.index++];
1093247829 if(!this.look) {
1094159 return;
1095 }
1096 //存下忽略的token
1097247670 if(S[this.look.type()]) {
1098109210 this.ignores[this.index - 1] = this.look;
1099 }
1100 //包括line的情况下要跳出
1101247670 if(this.look.type() == Token.LINE) {
110217784 this.line++;
110317784 this.col = 1;
110417784 this.hasMoveLine = true;
110517784 if(line) {
110635 break;
1107 }
1108 }
1109229886 else if(this.look.type() == Token.COMMENT) {
11102742 var s = this.look.content();
11112742 var n = character.count(this.look.content(), character.LINE);
11122742 if(n > 0) {
111390 this.line += n;
111490 var i = s.lastIndexOf(character.LINE);
111590 this.col += s.length - i - 1;
111690 this.hasMoveLine = true;
111790 if(line) {
11181 break;
1119 }
1120 }
1121 }
1122 else {
1123227144 this.col += this.look.content().length;
1124227144 if(!S[this.look.type()]) {
1125138460 break;
1126 }
1127 }
1128 } while(this.index <= this.length);
1129 },
1130 error: function(msg) {
113125 msg = 'SyntaxError: ' + (msg || ' syntax error');
113225 throw new Error(msg + ' line ' + this.lastLine + ' col ' + this.lastCol);
1133 },
1134 ignore: function() {
11358 return this.ignores;
1136 }
1137});
11381module.exports = Parser;

/Users/army/Sites/homunculus/src/util/Class.js

100%
24
24
0
LineHitsSource
11function inheritPrototype(subType, superType) {
210 var prototype = Object.create(superType.prototype);
310 prototype.constructor = subType;
410 subType.prototype = prototype;
5 //继承static变量
610 Object.keys(superType).forEach(function(k) {
737 subType[k] = superType[k];
8 });
910 return subType;
10}
111function wrap(fn) {
1219 fn.extend = function(sub) {
1310 inheritPrototype(sub, fn);
1410 return wrap(sub);
15 }
1619 fn.methods = function(o) {
1716 Object.keys(o).forEach(function(k) {
18179 fn.prototype[k] = o[k];
19 });
2016 return fn;
21 };
2219 fn.statics = function(o) {
238 Object.keys(o).forEach(function(k) {
24146 fn[k] = o[k];
25 });
268 return fn;
27 };
2819 return fn;
29}
301function klass(cons) {
319 return wrap(cons || function() {});
32}
331klass.extend = inheritPrototype;
341module.exports = klass;

/Users/army/Sites/homunculus/src/util/character.js

87%
41
36
5
LineHitsSource
11exports.LINE = '\n';
21exports.ENTER = '\r';
31exports.BLANK = ' ';
41exports.TAB = '\t';
51exports.UNDERLINE = '_';
61exports.DOLLAR = '$';
71exports.SHARP = '#';
81exports.MINUS = '-';
91exports.AT = '@';
101exports.SLASH = '/';
111exports.BACK_SLASH = '\\';
121exports.DECIMAL = '.';
131exports.LEFT_BRACKET = '[';
141exports.RIGHT_BRACKET = ']';
151exports.STAR = '*';
161exports.LEFT_PARENTHESE = '(';
171exports.RIGHT_PARENTHESE = ')';
181exports.COMMA = ',';
191exports.SEMICOLON = ';';
201exports.EQUAL = '=';
211exports.isDigit = function(c) {
220 return c >= '0' && c <= '9';
23};
241exports.isDigit16 = function(c) {
250 return exports.isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
26};
271exports.isDigit2 = function(c) {
280 return c == '0' || c == '1';
29};
301exports.isDigit8 = function(c) {
310 return c >= '0' && c <= '7';
32};
331exports.isLetter = function(c) {
34691 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
35};
361exports.count = function(s, c) {
37456707 var count = 0,
38 i = -1;
39456707 while((i = s.indexOf(c, i + 1)) != -1) {
4033562 count++;
41 }
42456707 return count;
43};
441exports.isUndefined = function(s) {
454387613 return typeof s == 'undefined';
46};
471exports.isString = function(s) {
480 return Object.prototype.toString.call(s) == "[object String]";
49};
501exports.isNumber = function(s) {
51456037 return Object.prototype.toString.call(s) == "[object Number]";
52};
\ No newline at end of file diff --git a/tests/es6parser.js b/tests/es6parser.js new file mode 100644 index 0000000..3fb8494 --- /dev/null +++ b/tests/es6parser.js @@ -0,0 +1,30 @@ +var homunculus = require('../'); + +var expect = require('expect.js'); +var fs = require('fs'); +var path = require('path'); + +var Token = homunculus.getClass('token'); +var Parser = homunculus.getClass('parser', 'js'); +var JsNode = homunculus.getClass('node', 'js'); + +function tree(node, arr) { + arr = arr || []; + var isToken = node.name() == JsNode.TOKEN; + var isVirtual = isToken && node.token().type() == Token.VIRTUAL; + if(isToken) { + if(!isVirtual) { + var token = node.token(); + arr.push(token.content()); + } + } + else { + arr.push(node.name()); + var childs = []; + arr.push(childs); + node.leaves().forEach(function(leaf, i) { + tree(leaf, childs); + }); + } + return arr; +} \ No newline at end of file diff --git a/tests/jscontext.js b/tests/jscontext.js index 6d35823..069f9a8 100644 --- a/tests/jscontext.js +++ b/tests/jscontext.js @@ -58,7 +58,7 @@ describe('jscontext', function() { }); it('fndecl params', function() { var context = homunculus.getContext('js'); - context.parse('function a(b, c = 1, ...d){}'); + context.parse('function a(b, c, d){}'); var a = context.getChild('a'); expect(a.hasParam('b')).to.be(true); expect(a.hasParam('c')).to.be(true); diff --git a/tests/jsparser.js b/tests/jsparser.js index 6183366..6f31f1c 100644 --- a/tests/jsparser.js +++ b/tests/jsparser.js @@ -176,24 +176,24 @@ describe('jsparser', function() { }); it('fndecl with params', function() { var parser = homunculus.getParser('js'); - var node = parser.parse('function a(b, c = 1, ...d) {}'); - expect(tree(node)).to.eql([JsNode.PROGRAM, [JsNode.FNDECL, ['function', 'a', '(', JsNode.FNPARAMS, ['b', ',', 'c', JsNode.BINDELEMENT, ['=', JsNode.PRMREXPR, ['1']], ',', JsNode.RESTPARAM, ['...', 'd']], ')', '{', JsNode.FNBODY, [], '}']]]); - }); - it('fndecl bindelem', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('function a(b, c = 1, ...d){}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,["b",",","c",JsNode.BINDELEMENT,["=",JsNode.PRMREXPR,["1"]],",",JsNode.RESTPARAM,["...","d"]],")","{",JsNode.FNBODY,[],"}"]]]); - }); - it('fndecl bindelem 2', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('function a(b, c = fn(...c)){}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,["b",",","c",JsNode.BINDELEMENT,["=",JsNode.CALLEXPR,[JsNode.PRMREXPR,["fn"],JsNode.ARGS,["(",JsNode.ARGLIST,[JsNode.BINDID,["...",JsNode.PRMREXPR,["c"]]],")"]]]],")","{",JsNode.FNBODY,[],"}"]]]); - }); - it('fndecl rest', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('function a(...b){}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,[JsNode.RESTPARAM,["...","b"]],")","{",JsNode.FNBODY,[],"}"]]]) - }); + var node = parser.parse('function a(b,c) {}'); + expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,["b",",","c"],")","{",JsNode.FNBODY,[],"}"]]]); + }); +// it('fndecl bindelem', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('function a(b, c = 1, ...d){}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,["b",",","c",JsNode.BINDELEMENT,["=",JsNode.PRMREXPR,["1"]],",",JsNode.RESTPARAM,["...","d"]],")","{",JsNode.FNBODY,[],"}"]]]); +// }); +// it('fndecl bindelem 2', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('function a(b, c = fn(...c)){}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,["b",",","c",JsNode.BINDELEMENT,["=",JsNode.CALLEXPR,[JsNode.PRMREXPR,["fn"],JsNode.ARGS,["(",JsNode.ARGLIST,[JsNode.BINDID,["...",JsNode.PRMREXPR,["c"]]],")"]]]],")","{",JsNode.FNBODY,[],"}"]]]); +// }); +// it('fndecl rest', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('function a(...b){}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.FNDECL,["function","a","(",JsNode.FNPARAMS,[JsNode.RESTPARAM,["...","b"]],")","{",JsNode.FNBODY,[],"}"]]]) +// }); it('fndecl error 1', function() { var parser = homunculus.getParser('js'); expect(function() { @@ -280,16 +280,33 @@ describe('jsparser', function() { parser.parse('.'); }).to.throwError(); }); - it('array spread in prmrexpr', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('[1, 2, ...gen()]'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.EXPRSTMT,[JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],",",JsNode.PRMREXPR,["2"],",",JsNode.SPREAD,["...",JsNode.CALLEXPR,[JsNode.PRMREXPR,["gen"],JsNode.ARGS,["(",")"]]],"]"]]]]]); - }); +// it('array spread in prmrexpr', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('[1, 2, ...gen()]'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.EXPRSTMT,[JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],",",JsNode.PRMREXPR,["2"],",",JsNode.SPREAD,["...",JsNode.CALLEXPR,[JsNode.PRMREXPR,["gen"],JsNode.ARGS,["(",")"]]],"]"]]]]]); +// }); it('arrltr', function() { var parser = homunculus.getParser('js'); var node = parser.parse('[,,,2,3,]'); expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.EXPRSTMT,[JsNode.PRMREXPR,[JsNode.ARRLTR,["[",",",",",",",JsNode.PRMREXPR,["2"],",",JsNode.PRMREXPR,["3"],",","]"]]]]]); }); + it('arrltr error', function() { + var parser = homunculus.getParser('js'); + expect(function() { + parser.parse('[,,,2,3,'); + }).to.throwError(); + }); + it('objltr', function() { + var parser = homunculus.getParser('js'); + var node = parser.parse('var o = {a: [], "b": 3, 5: {}}'); + expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,["o",JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.OBJLTR,["{",JsNode.PROPTASSIGN,["a",":",JsNode.PRMREXPR,[JsNode.ARRLTR,["[","]"]]],",",JsNode.PROPTASSIGN,["\"b\"",":",JsNode.PRMREXPR,["3"]],",",JsNode.PROPTASSIGN,["5",":",JsNode.PRMREXPR,[JsNode.OBJLTR,["{","}"]]],"}"]]]]]]]); + }); + it('objltr error', function() { + var parser = homunculus.getParser('js'); + expect(function() { + parser.parse('var o = {+'); + }).to.throwError(); + }); it('mmbexpr 1', function() { var parser = homunculus.getParser('js'); var node = parser.parse('a.b'); @@ -691,156 +708,156 @@ describe('jsparser', function() { var node = parser.parse(); expect(tree(node)).to.eql([JsNode.PROGRAM,[]]); }); - it('letstmt 1', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('let a'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.LETSTMT,["let",JsNode.VARDECL,["a"]]]]); - }); - it('letstmt 2', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('let a, b = 1'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.LETSTMT,["let",JsNode.VARDECL,["a"],",",JsNode.VARDECL,["b",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]]]]); - }); - it('cststmt 1', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('const a'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,["a"]]]]); - }); - it('cststmt 2', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('const a, b = 1'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,["a"],",",JsNode.VARDECL,["b",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]]]]); - }); - it('class decl', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{;}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[';'],"}"]]]); - }); - it('class extend', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{}class B extends A{}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[],"}"],JsNode.CLASSDECL,["class","B",JsNode.HERITAGE,["extends","A"],"{",JsNode.CLASSBODY,[],"}"]]]); - }); - it('class method', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{ method(param){} }'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["method","(",JsNode.FNPARAMS,["param"],")","{",JsNode.FNBODY,[],"}"]],"}"]]]); - }); - it('class static method', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{ static method(){} }'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,["static",JsNode.METHOD,["method","(",")","{",JsNode.FNBODY,[],"}"]],"}"]]]); - }); - it('class error', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('class A'); - }).to.throwError(); - }); - it('class method error', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('class A{ method'); - }).to.throwError(); - }); - it('class method duplicate error', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('class A{ A(){} A(){} }'); - }).to.throwError(); - }); - it('class static error', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('class A{ static'); - }).to.throwError(); - }); - it('super in class', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A extends B{constructor(){super()}}') - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A",JsNode.HERITAGE,["extends","B"],"{",JsNode.CLASSBODY,[JsNode.METHOD,["constructor","(",")","{",JsNode.FNBODY,[JsNode.SUPERSTMT,["super",JsNode.ARGS,["(",")"]]],"}"]],"}"]]]); - }); - it('super recursion', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A extends B{method(){super.super.a()}}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A",JsNode.HERITAGE,["extends","B"],"{",JsNode.CLASSBODY,[JsNode.METHOD,["method","(",")","{",JsNode.FNBODY,[JsNode.SUPERSTMT,["super",".","super",".","a",JsNode.ARGS,["(",")"]]],"}"]],"}"]]]); - }); - it('super out class', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('super()'); - }).to.throwError(); - }); - it('set', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{set a(p){}}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["set",JsNode.SETFN,[JsNode.PROPTNAME,["a"],"(",JsNode.PROPTSETS,["p"],")","{",JsNode.FNBODY,[],"}"]]],"}"]]]); - }); - it('get', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('class A{get a(){}}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["get",JsNode.GETFN,[JsNode.PROPTNAME,["a"],"(",")","{",JsNode.FNBODY,[],"}"]]],"}"]]]); - }); - it('destructuring array', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('var [a] = [1]'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["a"],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],"]"]]]]]]]); - }); - it('destructuring with restparam', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('let [a, ...b] = [1, 2, 3]'); - expect(tree(node)).to.eql([JsNode.PROGRAM, [JsNode.LETSTMT, ["let", JsNode.VARDECL, [JsNode.ARRBINDPAT, ["[", JsNode.SINGLENAME, ["a"], ",", JsNode.RESTPARAM, ["...", "b"], "]"], JsNode.ASSIGN, ["=", JsNode.PRMREXPR, [JsNode.ARRLTR, ["[", JsNode.PRMREXPR, ["1"], ",", JsNode.PRMREXPR, ["2"], ",", JsNode.PRMREXPR, ["3"], "]"]]]]]]]); - }); - it('destructuring with default value', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('const [a = 1, [b] = [2]] = [, []]'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["a",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]],",",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["b"],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["2"],"]"]]]],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",",",JsNode.PRMREXPR,[JsNode.ARRLTR,["[","]"]],"]"]]]]]]]); - }); - it('destructuring object', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('var {x, y = 1, "f": [z]} = {}'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,[JsNode.SINGLENAME,["x"]],",",JsNode.BINDPROPT,[JsNode.SINGLENAME,["y",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]],",",JsNode.BINDPROPT,["\"f\"",":",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["z"],"]"]]],"}"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.OBJLTR,["{","}"]]]]]]]); - }); - it('destructuring complex', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('var [x, {"a":[y=1,{z=2},...o]}] = []'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["x"],",",JsNode.BINDELEM,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,["\"a\"",":",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["y",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]],",",JsNode.BINDELEM,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,[JsNode.SINGLENAME,["z",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["2"]]]],"}"]],",",JsNode.RESTPARAM,["...","o"],"]"]]],"}"]],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[","]"]]]]]]]); - }); - it('destructuring without assign should throw error', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('var [a];'); - }).to.throwError(); - }); - it('destructuring object error 1', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('var {'); - }).to.throwError(); - }); - it('destructuring object error 2', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('var {a'); - }).to.throwError(); - }); - it('destructuring object error 3', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('var {!'); - }).to.throwError(); - }); - it('destructuring object error 4', function() { - var parser = homunculus.getParser('js'); - expect(function() { - parser.parse('var {a '); - }).to.throwError(); - }); - it('binding id in call', function() { - var parser = homunculus.getParser('js'); - var node = parser.parse('Math.max(...[1, 2])'); - expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.EXPRSTMT,[JsNode.CALLEXPR,[JsNode.MMBEXPR,[JsNode.PRMREXPR,["Math"],".","max"],JsNode.ARGS,["(",JsNode.ARGLIST,[JsNode.BINDID,["...",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],",",JsNode.PRMREXPR,["2"],"]"]]]],")"]]]]]); - }); +// it('letstmt 1', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('let a'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.LETSTMT,["let",JsNode.VARDECL,["a"]]]]); +// }); +// it('letstmt 2', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('let a, b = 1'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.LETSTMT,["let",JsNode.VARDECL,["a"],",",JsNode.VARDECL,["b",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]]]]); +// }); +// it('cststmt 1', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('const a'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,["a"]]]]); +// }); +// it('cststmt 2', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('const a, b = 1'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,["a"],",",JsNode.VARDECL,["b",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]]]]); +// }); +// it('class decl', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{;}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[';'],"}"]]]); +// }); +// it('class extend', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{}class B extends A{}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[],"}"],JsNode.CLASSDECL,["class","B",JsNode.HERITAGE,["extends","A"],"{",JsNode.CLASSBODY,[],"}"]]]); +// }); +// it('class method', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{ method(param){} }'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["method","(",JsNode.FNPARAMS,["param"],")","{",JsNode.FNBODY,[],"}"]],"}"]]]); +// }); +// it('class static method', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{ static method(){} }'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,["static",JsNode.METHOD,["method","(",")","{",JsNode.FNBODY,[],"}"]],"}"]]]); +// }); +// it('class error', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('class A'); +// }).to.throwError(); +// }); +// it('class method error', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('class A{ method'); +// }).to.throwError(); +// }); +// it('class method duplicate error', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('class A{ A(){} A(){} }'); +// }).to.throwError(); +// }); +// it('class static error', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('class A{ static'); +// }).to.throwError(); +// }); +// it('super in class', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A extends B{constructor(){super()}}') +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A",JsNode.HERITAGE,["extends","B"],"{",JsNode.CLASSBODY,[JsNode.METHOD,["constructor","(",")","{",JsNode.FNBODY,[JsNode.SUPERSTMT,["super",JsNode.ARGS,["(",")"]]],"}"]],"}"]]]); +// }); +// it('super recursion', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A extends B{method(){super.super.a()}}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A",JsNode.HERITAGE,["extends","B"],"{",JsNode.CLASSBODY,[JsNode.METHOD,["method","(",")","{",JsNode.FNBODY,[JsNode.SUPERSTMT,["super",".","super",".","a",JsNode.ARGS,["(",")"]]],"}"]],"}"]]]); +// }); +// it('super out class', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('super()'); +// }).to.throwError(); +// }); +// it('set', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{set a(p){}}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["set",JsNode.SETFN,[JsNode.PROPTNAME,["a"],"(",JsNode.PROPTSETS,["p"],")","{",JsNode.FNBODY,[],"}"]]],"}"]]]); +// }); +// it('get', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('class A{get a(){}}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CLASSDECL,["class","A","{",JsNode.CLASSBODY,[JsNode.METHOD,["get",JsNode.GETFN,[JsNode.PROPTNAME,["a"],"(",")","{",JsNode.FNBODY,[],"}"]]],"}"]]]); +// }); +// it('destructuring array', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('var [a] = [1]'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["a"],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],"]"]]]]]]]); +// }); +// it('destructuring with restparam', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('let [a, ...b] = [1, 2, 3]'); +// expect(tree(node)).to.eql([JsNode.PROGRAM, [JsNode.LETSTMT, ["let", JsNode.VARDECL, [JsNode.ARRBINDPAT, ["[", JsNode.SINGLENAME, ["a"], ",", JsNode.RESTPARAM, ["...", "b"], "]"], JsNode.ASSIGN, ["=", JsNode.PRMREXPR, [JsNode.ARRLTR, ["[", JsNode.PRMREXPR, ["1"], ",", JsNode.PRMREXPR, ["2"], ",", JsNode.PRMREXPR, ["3"], "]"]]]]]]]); +// }); +// it('destructuring with default value', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('const [a = 1, [b] = [2]] = [, []]'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.CSTSTMT,["const",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["a",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]],",",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["b"],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["2"],"]"]]]],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",",",JsNode.PRMREXPR,[JsNode.ARRLTR,["[","]"]],"]"]]]]]]]); +// }); +// it('destructuring object', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('var {x, y = 1, "f": [z]} = {}'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,[JsNode.SINGLENAME,["x"]],",",JsNode.BINDPROPT,[JsNode.SINGLENAME,["y",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]]],",",JsNode.BINDPROPT,["\"f\"",":",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["z"],"]"]]],"}"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.OBJLTR,["{","}"]]]]]]]); +// }); +// it('destructuring complex', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('var [x, {"a":[y=1,{z=2},...o]}] = []'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.VARSTMT,["var",JsNode.VARDECL,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["x"],",",JsNode.BINDELEM,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,["\"a\"",":",JsNode.BINDELEM,[JsNode.ARRBINDPAT,["[",JsNode.SINGLENAME,["y",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["1"]]],",",JsNode.BINDELEM,[JsNode.OBJBINDPAT,["{",JsNode.BINDPROPT,[JsNode.SINGLENAME,["z",JsNode.ASSIGN,["=",JsNode.PRMREXPR,["2"]]]],"}"]],",",JsNode.RESTPARAM,["...","o"],"]"]]],"}"]],"]"],JsNode.ASSIGN,["=",JsNode.PRMREXPR,[JsNode.ARRLTR,["[","]"]]]]]]]); +// }); +// it('destructuring without assign should throw error', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('var [a];'); +// }).to.throwError(); +// }); +// it('destructuring object error 1', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('var {'); +// }).to.throwError(); +// }); +// it('destructuring object error 2', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('var {a'); +// }).to.throwError(); +// }); +// it('destructuring object error 3', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('var {!'); +// }).to.throwError(); +// }); +// it('destructuring object error 4', function() { +// var parser = homunculus.getParser('js'); +// expect(function() { +// parser.parse('var {a '); +// }).to.throwError(); +// }); +// it('binding id in call', function() { +// var parser = homunculus.getParser('js'); +// var node = parser.parse('Math.max(...[1, 2])'); +// expect(tree(node)).to.eql([JsNode.PROGRAM,[JsNode.EXPRSTMT,[JsNode.CALLEXPR,[JsNode.MMBEXPR,[JsNode.PRMREXPR,["Math"],".","max"],JsNode.ARGS,["(",JsNode.ARGLIST,[JsNode.BINDID,["...",JsNode.PRMREXPR,[JsNode.ARRLTR,["[",JsNode.PRMREXPR,["1"],",",JsNode.PRMREXPR,["2"],"]"]]]],")"]]]]]); +// }); }); describe('js lib exec test', function() { it('jquery 1.11.0', function() { diff --git a/web/parser/es6/Node.js b/web/parser/es6/Node.js new file mode 100644 index 0000000..88a9e86 --- /dev/null +++ b/web/parser/es6/Node.js @@ -0,0 +1,165 @@ +define(function(require, exports, module) { + 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/web/parser/es6/Parser.js b/web/parser/es6/Parser.js new file mode 100644 index 0000000..ef61e99 --- /dev/null +++ b/web/parser/es6/Parser.js @@ -0,0 +1,1536 @@ +define(function(require, exports, module) { + 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/web/parser/js/Node.js b/web/parser/js/Node.js index ee8e485..88a9e86 100644 --- a/web/parser/js/Node.js +++ b/web/parser/js/Node.js @@ -140,8 +140,11 @@ define(function(require, exports, module) { 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'); diff --git a/web/parser/js/Parser.js b/web/parser/js/Parser.js index 69bc6b4..f008c1c 100644 --- a/web/parser/js/Parser.js +++ b/web/parser/js/Parser.js @@ -56,9 +56,6 @@ define(function(require, exports, module) { if(this.look.content() == 'function') { return this.fndecl(); } - else if(this.look.content() == 'class') { - return this.classdecl(); - } else { return this.stmt(allowSuper); } @@ -68,10 +65,6 @@ define(function(require, exports, module) { this.error(); } switch(this.look.content()) { - case 'let': - return this.letstmt(); - case 'const': - return this.cststmt(); case 'var': return this.varstmt(); case '{': @@ -100,13 +93,6 @@ define(function(require, exports, module) { 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++) { @@ -129,40 +115,6 @@ define(function(require, exports, module) { 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( @@ -200,94 +152,6 @@ define(function(require, exports, module) { } 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('=')); @@ -601,42 +465,6 @@ define(function(require, exports, module) { ); 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( @@ -684,36 +512,12 @@ define(function(require, exports, module) { }, 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) { @@ -723,76 +527,6 @@ define(function(require, exports, module) { } 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); @@ -1237,11 +971,6 @@ define(function(require, exports, module) { } return node; }, - bindid: function() { - var node = new Node(Node.BINDID); - node.add(this.match('...'), this.assignexpr()); - return node; - }, arrltr: function() { var node = new Node(Node.ARRLTR); node.add(this.match('[')); @@ -1262,11 +991,6 @@ define(function(require, exports, module) { 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('{')); @@ -1325,51 +1049,6 @@ define(function(require, exports, module) { } 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('(')); @@ -1384,15 +1063,12 @@ define(function(require, exports, module) { }, 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) {