将以下代码拷贝到新的HTML文件中打开即可看到效果!
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>JS语法检查器</title>
<style>
.js-color-reservedword {
color: #0823fc;
}
.js-color-identifier {
color: #ff8013;
}
.js-color-exception {
text-decoration: underline;
color: red;
}
.js-color-numericliteral {
color: #6df407;
}
.js-color-stringliteral {
color: #fc9090;
}
.js-color-comment {
color: green;
}
.js-color-functionname {
color: #f8f408;
}
.js-color-punctuator {
color: #e804fa;
}
.js-color-regularexpressionliteral {
color: #73b2ee;
}
.js-color-linecount {
color: pink;
}
.js-code-line-border {
border: 1px solid #cbeeb0;
}
.code-text, .code-div {
width: 600px;
height: 600px;
}
div, span {
margin: 0px;
padding: 0px;
}
pre{
margin: 0;
padding: 0;
display: block;
}
.js-code-edit, textarea {
font-size: 15px;
color: black;
background-color: white;
width: 49%;
height: 600px;
float: left;
border: none;
outline: 1px solid black;
overflow: auto;
margin-left: 0.8%;
}
button {
width: 240px;
height: 50px;
margin-top: 20px;
}
</style>
</head>
<body>
<textarea spellcheck="false">
function paintAST(ast){
var result = "<pre>" + _paintAST(ast) + "</pre>";
return result.replace(/<pre><\/pre>/, ‘<pre><br></pre>‘);
function _paintAST(ast){
var str = ‘‘;
for(var name in ast){
if(_typeof(ast[name], ‘Object‘) && ast[name].value){
if(ast[name].type == ‘LineTerminator‘){ str += ast[name].value.replace(/\s/g, ‘</pre><pre>‘); }
else{
var className = ‘js-color-‘ + ast[name].type.toLowerCase(),
value = ast[name].value.replace(/</g, ‘<‘).replace(/>/g, ‘>‘);
str += ‘<span class="‘ + className +‘" ‘ + (ast[name].msg? ‘title="‘ + ast[name].msg + ‘"‘: ‘‘) +‘>‘ + value + ‘</span>‘;
}
}
else if(_typeof(ast[name], ‘Array‘) || _typeof(ast[name], ‘Object‘)){ str += _paintAST(ast[name]); }
}
return str;
}
function _typeof(obj, type){
return Object.prototype.toString.call(obj).indexOf(type) > -1;
}
}
</textarea>
<div class="js-code-edit" spellcheck="false"></div>
<button>Test</button>
<script>
var btn = document.querySelector(‘button‘);
document.querySelector(‘.js-code-edit‘).innerHTML = JsCodeEdit(document.querySelector(‘textarea‘).value);
btn.onclick = function (e) {
//var sTime = Date.now();
document.querySelector(‘.js-code-edit‘).innerHTML = JsCodeEdit(document.querySelector(‘textarea‘).value);
//alert((Date.now() - sTime));
}
function JsCodeEdit(string) {
var ast = parserJSCode(string);
return paintAST(ast);//将表达式树转换成HTML
return printAST(ast);//返回表达式树
}
function printAST(ast) {
return JSON.stringify(ast);
}
function paintAST(ast){
var result = "<pre>" + _paintAST(ast) + "</pre>";
return result.replace(/<pre><\/pre>/, ‘<pre><br></pre>‘);
function _paintAST(ast){
var str = ‘‘;
for(var name in ast){
if(_typeof(ast[name], ‘Object‘) && ast[name].value){
if(ast[name].type == ‘LineTerminator‘){ str += ast[name].value.replace(/\s/g, ‘</pre><pre>‘); }
else{
var className = ‘js-color-‘ + ast[name].type.toLowerCase(),
value = ast[name].value.replace(/</g, ‘<‘).replace(/>/g, ‘>‘);
str += ‘<span class="‘ + className +‘" ‘ + (ast[name].msg? ‘title="‘ + ast[name].msg + ‘"‘: ‘‘) +‘>‘ + value + ‘</span>‘;
}
}
else if(_typeof(ast[name], ‘Array‘) || _typeof(ast[name], ‘Object‘)){ str += _paintAST(ast[name]); }
}
return str;
}
function _typeof(obj, type){
return Object.prototype.toString.call(obj).indexOf(type) > -1;
}
}
function parserJSCode(string) {
if (!string || typeof string != ‘string‘) { return { AST: { type: ‘Program‘, body: [] } }; }
var globalVariable = { strictModel: false, currInstance: ‘‘ };
var AST = { type: ‘Program‘, body: [] }, i = 0;
var PARSER_FINSH = ‘Parser js code is finshed!‘;
var startFun = new Function();
try{ startFun.program(AST.body);
}
catch(e){
if(e != PARSER_FINSH){ console.error(e); }
}
return AST;
function peek(parent, getReg) {
if(eol()){ throw PARSER_FINSH; }
var haveLineTerminator = false;
do{
var space = getSpace(),
terminator = getLineTerminator(),
comm = comment();
pushItem(parent, space), pushItem(parent, terminator), pushItem(parent, comm);
if(!space && ! terminator && !comm){ break; }
}while(!eol());
if(eol()){ throw PARSER_FINSH; }
var returnObj = identifierName() || stringLiteral() || numericLiteral() || punctuator() ||
(getReg && regularExpressionLiteral()) || divPunctuator() || unknowToken();
returnObj.haveLineTerminator = haveLineTerminator;
return returnObj;
function stringLiteral() {
var str = { type: ‘‘, value: ‘‘, msg: ‘‘ };
if (/[‘"‘]/g.test(string[i])) {
str.type = ‘StringLiteral‘, str.value += string[i];
var ch = string[i];
for (i++; !eol() && string[i] != ch && !isLineTerminator(string[i]) ; i++) {
if (string[i] == ‘\\‘) {
str.value += string[i] + (string[++i] ? string[i] : ‘‘);
var len = (string[i] == ‘u‘ ? 4 : string[i] == ‘x‘ ? 2 : 0)
for (i++; !eol() && len > 0 && string[i] != ch; len--, i++) {
str.value += string[i];
if (!isHex(string[i])) { str.msg = len == 2 ? ‘Invalid Hex const!‘ : ‘Invalid Unicode const!‘; }
}
i--;
}
else { str.value += string[i]; }
}
if (string[i] == ch) { str.value += ch; i++; }
else { str.msg = ‘add ‘ + ch + ‘ to end string!‘; }
}
return str.value ? str : ‘‘;
}
function identifierName() {
var identifier = { type: ‘‘, value: ‘‘, msg: ‘‘ };
if (!(/[a-zA-Z_$\\]/g.test(string[i]))) { return ‘‘; }
identifier.type = ‘IdentifierName‘, identifier.value += string[i] == ‘\\‘ ? ‘‘ : string[i];
for (string[i] == ‘\\‘ ? i : i++; !eol() && /[a-zA-Z_$\d\\]/g.test(string[i]) ; i++) {
identifier.value += string[i];
if (string[i] == ‘\\‘) {
if (string[i + 1] != ‘u‘) { identifier.msg = ‘Invalid Unicode!‘; continue; }
identifier.value += string[++i];
var count = 0;
for (i++; !eol() && isHex(string[i]) ; count++, i++) { identifier.value += string[i]; }
i--;
identifier.msg = (count < 4 ? ‘Invalid Unicode!‘ : ‘‘);
}
}
if (identifier.value && isReservedWord(identifier.value)) { identifier.type = ‘Reservedword‘; }
else if (identifier.value) { identifier.type = ‘Identifier‘; }
return identifier.value ? identifier : ‘‘;
}
function numericLiteral() {
var numeric = { type: ‘‘, value: ‘‘, msg: ‘‘ };
//if (!(/[.\d+-]/g.test(string[i])) || (string[i] == ‘.‘ && !isDigit(string[i + 1]))) { return ‘‘; }
//if(/[+-]/g.test(string[i])){
//if (!(/[.\d]/g.test(string[i + 1])) || (string[i + 1] == ‘.‘ && !isDigit(string[i + 2]))) { return ‘‘; }
//numeric.value += string[i++];
//}
if (!(/[.\d]/g.test(string[i])) || (string[i] == ‘.‘ && !isDigit(string[i + 1]))) { return ‘‘; }
numeric.type = ‘NumericLiteral‘, numeric.value += string[i];
if (string[i] == ‘0‘ && /[xX]/g.test(string[i + 1])) {
var hex = string[++i];
for (i++; !eol() && isHex(string[i]) ; i++) { hex += string[i]; }
if (hex.length > 1) { numeric.value += hex; }
else { i--; }
return numeric.value ? numeric : ‘‘;
}
var numericArr = [‘‘, ‘‘, ‘‘, ‘‘], firstZero = string[i] == ‘0‘;
for (i++; !eol() && isDigit(string[i]) ; i++) { numericArr[0] += string[i]; }
if (string[i] == ‘.‘) {
numericArr[1] = ‘.‘;
numeric.msg = (firstZero && numericArr[0] ? ‘Invalid numeric!‘ : ‘‘);
i++;
}
for (; !eol() && isDigit(string[i]) ; i++) { numericArr[2] += string[i]; }
if (/[eE]/g.test(string[i])) {
if (/[^\d+-]/g.test(string[i + 1])) {
numeric.value += numericArr.join(‘‘);
return numeric.value ? numeric : ‘‘;
}
else if (/[+-]/g.test(string[i + 1]) && !isDigit(string[i + 2])) {
numeric.value += numericArr.join(‘‘);
return numeric.value ? numeric : ‘‘;
}
numericArr[3] += string[i] + string[++i];
for (i++; !eol() && isDigit(string[i]) ; i++) { numericArr[3] += string[i]; }
}
numeric.value += numericArr.join(‘‘);
return numeric.value ? numeric : ‘‘;
}
function punctuator() {
var punc = { type: ‘‘, value: ‘‘, msg: ‘‘ };
if (/[\[\]{}().,;:\?~]/g.test(string[i])) { return { type: ‘Punctuator‘, value: string[i++], msg: ‘‘ } }
var sI = i, str = ‘‘;
str = string[i] + string[i + 1] + string[i + 2] + string[i + 3];
if (str == ‘>>>=‘) { punc.value = str; }
else {
str = string[i] + string[i + 1] + string[i + 2];
if ([‘===‘, ‘!==‘, ‘>>>‘, ‘<<=‘, ‘>>=‘].indexOf(str) > -1) { punc.value = str; }
else {
str = string[i] + string[i + 1];
if ([‘<=‘, ‘>=‘, ‘==‘, ‘!=‘, ‘++‘, ‘--‘, ‘>>‘, ‘<<‘, ‘&&‘, ‘||‘, ‘+=‘, ‘-=‘, ‘*=‘, ‘%=‘, ‘&=‘, ‘|=‘, ‘^=‘].indexOf(str) > -1) { punc.value = str; }
else {
str = string[i];
if ([‘=‘, ‘+‘, ‘-‘, ‘*‘, ‘%‘, ‘&‘, ‘|‘, ‘^‘, ‘!‘, ‘>‘, ‘<‘].indexOf(str) > -1) { punc.value = str; }
else { str = ‘‘; }
}
}
}
i += str.length;
if (punc.value) { punc.type = ‘Punctuator‘; }
return punc.value ? punc : ‘‘;
}
function divPunctuator() {
if (string[i] == ‘/‘) {
if (string[++i] == ‘=‘) { i++; return { type: ‘Punctuator‘, value: ‘/=‘, msg: ‘‘ }; }
return { type: ‘Punctuator‘, value: ‘/‘, msg: ‘‘ };
}
return false;
}
function regularExpressionLiteral() {
if (string[i] != ‘/‘ || /[*\/]/.test(string[i + 1]) || isLineTerminator(string[i + 1])) { return ‘‘; }
return getRegExp(string[i]);
function getRegExp(ch) {
var regExp = { type: ‘RegularExpressionLiteral‘, value: string[i++], msg: ‘‘ };
while (!eol() && string[i] != ‘/‘ && string[i] != ch && !isLineTerminator(string[i])) {
if (string[i] == ‘\\‘) {
regExp.value += string[i++];
if (isLineTerminator(string[i])) { regExp.msg = ‘Error RegularExpressionLiteral‘; break; }
regExp.value += string[i++];
}
else if (/[\[\(]/g.test(string[i])) {
var ch = string[i] == ‘[‘ ? ‘]‘ : ‘)‘;
var r = getRegExp(ch);
ch = ‘/‘;
regExp.value += r.value;
regExp.msg = r.msg;
if (r.end) { return regExp; }
}
else { regExp.value += string[i++]; }
}
if ((ch == ‘]‘ && string[i] == ch) || (ch == ‘)‘ && string[i] == ch)) { regExp.value += string[i++] }
else if (string[i] == ‘/‘) {
regExp.value += string[i++]
while (!eol() && /[a-zA-Z\d$_]/g.test(string[i])) {
regExp.value += string[i]
if (!(/[gim]/g.test(string[i++]))) { regExp.msg = ‘Invaild Character‘; }
}
regExp.end = true;
}
else { regExp.msg = ‘Error RegularExpressionLiteral‘; regExp.end = true; }
return regExp;
}
}
function comment() {
var comment = { type: ‘Comment‘, value: ‘‘, msg: ‘‘ };
if (string[i] == ‘/‘ && string[i + 1] == ‘/‘) {
comment.value += ‘//‘;// comment.type = ‘SingleLineComment‘;
for (i += 2; !eol() && !isLineTerminator(string[i]) ; i++) { comment.value += string[i]; }
}
else if (string[i] == ‘/‘ && string[i + 1] == ‘*‘) {
comment.value += ‘/*‘;// comment.type = ‘MuliteLineComment‘;
for (i += 2; !eol() && (string[i] != ‘*‘ || string[i + 1] != ‘/‘) ; i++) { comment.value += string[i]; }
comment.value += eol() ? ‘‘ : ‘*/‘;
i += 2;
}
return comment.value ? comment : ‘‘;
}
function unknowToken(){
return { type: ‘UnknowToken‘, value: string[i++], msg: ‘Exception UnknowToken!‘ };
}
function getSpace(){
var space = { type: ‘Space‘, value: ‘‘, msg: ‘‘ };
while(!eol() && isSpace(string[i])){
space.value += string[i++];
}
return space.value? space: ‘‘;
}
function getLineTerminator(){
var terminator = { type: ‘LineTerminator‘, value: ‘‘, msg: ‘‘ };
while(!eol() && isLineTerminator(string[i])){
haveLineTerminator = true;
terminator.value += string[i++];
}
return terminator.value ? terminator: ‘‘;
}
}
function Expression(parent, p) {
this.expression = expression;
this.assignmentExpression = assignmentExpression;
this.leftHandSideExpression = leftHandSideExpression;
function primaryExpression(parent, p) {
if (p && equal(p.value, ‘/‘)) { i--; p = null; }
p = p ? p : peek(parent, true);
var op = isItemInArray([‘NumericLiteral‘, ‘StringLiteral‘, ‘Identifier‘, ‘RegularExpressionLiteral‘], p.type) ? p.type : p.value;
switch (op) {
case ‘this‘: pushItem(parent, p); break;
case ‘true‘:
case ‘false‘: pushItem(parent, p); break;
case ‘null‘: pushItem(parent, p); break;
case ‘Identifier‘: pushItem(parent, p); break;
case ‘StringLiteral‘:
case ‘RegularExpressionLiteral‘:
case ‘NumericLiteral‘: pushItem(parent, p); break;
case ‘[‘: return arrayLiteral(parent, p);
case ‘{‘: return objectLiteral(parent, p);
case ‘(‘: return argument(parent, p, true);
default: return p;//return exception
}
}
function arrayLiteral(parent, p) {
var arrayLiteral = { type: ‘ArrayLiteral‘, items: [] };
parent.push(arrayLiteral), parent = arrayLiteral.items;
pushItem(parent, p);
do {
p = peek(parent);
if (equal(p.value, ‘,‘)) { p = pushItem(parent, p); }
else if (equal(p.value, ‘]‘)) { return pushItem(parent, p); }
else {
p = assignmentExpression(parent, p);
if(p){
if(equal(p.value, ‘]‘)){ return pushItem(parent, p); }
else if (equal(p.value, ‘,‘)) { p = pushItem(parent, p); }
else{ p = pushItem(parent, p, ‘Invalid Character!‘); }
}
}
}
while (true);
}
//function elementList(){ }
function objectLiteral(parent, p) {
var obj = { type: ‘ObjectLiteral‘, child: [] };
parent.push(obj), parent = obj.child;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
else{
do{
p = propertyAssignment(parent, p) || peek(parent);
if(equal(p.value, ‘,‘)){
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
}
else if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
else{ p = pushItem(parent, p, ‘Excepted }‘); }
}while(true);
}
}
//function propertyNameAndValueList(parent, p) { }
function propertyAssignment(parent, p) {
var propertyName = [‘Identifier‘, ‘Reservedword‘, ‘StringLiteral‘, ‘NumericLiteral‘];
p = p? p: peek(parent);
if(propertyName.indexOf(p.type) == -1){ p.msg = p.msg || ‘Invalid Proerty Name!‘; }
if(equal(p.value, ‘get‘) || equal(p.value, ‘set‘)){ return _GetAndSet(p.value, p); }
else{
p = pushItem(parent, p) || peek(parent);
if(!equal(p.value, ‘:‘)){ p.msg = p.msg || ‘Excepted :‘; }
else{ p = pushItem(parent, p); }
return assignmentExpression(parent, p);
}
function _GetAndSet(functionName, p){
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘:‘)){
p = pushItem(parent, p);
return assignmentExpression(parent, p);
}
else if(propertyName.indexOf(p.type) == -1){ p.msg = p.msg || ‘Invalid‘ + functionName + ‘property name!‘; }
else { p = pushItem(parent, p); }
p = p? p: peek(parent);
if(!equal(p.value, ‘(‘)){ p.msg = p.msg || ‘Excepted (‘; }
else{ p = pushItem(parent, p); }
p = p? p: peek(parent);
if(equal(functionName, ‘set‘)){
if(!equal(p.type, ‘Identifier‘)){ p.msg = p.msg || ‘Set property must one parameter!‘; }
else{ p = pushItem(parent, p); }
}
p = p? p: peek(parent);
if(!equal(p.value, ‘)‘)){ p.msg = p.msg || ‘Excepted )‘; }
else{ p = pushItem(parent, p); }
p = p? p: peek(parent);
if(!equal(p.value, ‘{‘)){ p.msg = p.msg || ‘Excepted {‘; }
else{ p = pushItem(parent, p); }
p = (new Function()).functionBody(parent, p) || peek(parent);
if(!equal(p.value, ‘}‘)){ p.msg = p.msg || ‘Excepted }‘; }
else{ p = pushItem(parent, p); }
return p;
}
}
//function propertyName(parent, p) { }
function memberExpression(parent, p, isNewExpressionCall) {
p = primaryExpression(parent, p);
if(p){
if(equal(p.value, ‘function‘)){ p = (new Function()).functionExpression(parent, p); }
else if(equal(p.value, ‘new‘)){
p = pushItem(parent, p);
p = memberExpression(parent, p);
return argument(parent, p);
}
else{
p.msg = p.msg || ‘Excepted primary expression!‘;
return p;
}
}
else{ p = peek(parent); }
return memberExtendExpression(parent, p);
}
function newExpression(parent, p) {
while(equal(p.value, ‘new‘)){
p = pushItem(parent, p) || peek(parent);
}
return memberExpression(parent, p);
}
function callExpression(parent, p) {
p = argument(parent, p);
return memberExtendExpression(parent, p);
}
function argument(parent, p, primaryCall) {
setFunctionType(parent);
var arg = {type: ‘Argumments‘, child: []};
parent.push(arg), parent = arg.child;
p = p? p: peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted (‘; }
p = p? p: peek(parent);
if(!equal(p.value, ‘)‘)){
p = argumentList(parent, p) || peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else {
p.msg = p.msg || ‘Excepted )‘;
return p;
}
}
else{ p = pushItem(parent, p, primaryCall? ‘Excepted expression!‘: ‘‘); }
}
function memberExtendExpression(parent, p){
p = p? p: peek(parent);
while([‘(‘, ‘[‘, ‘.‘].indexOf(p.value) > -1){
if(equal(p.value, ‘(‘)){ p = argument(parent, p); }
else if(equal(p.value, ‘[‘)){
pushItem(parent, p);
p = expression(parent) || peek(parent);
if(equal(p.value, ‘]‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted ]‘; }
}
else{
p = pushItem(parent, p) || peek(parent);
if(equal(p.type, ‘Identifier‘) || equal(p.type, ‘Reservedword‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted IdentifierName!‘; }
}
p = p? p: peek(parent);
}
return p;
}
function setFunctionType(parent, part){
var parts = [‘Comment‘, ‘WhiteSpace‘, ‘Lineterminate‘];
for(var index = parent.length - 1; index > -1 && parts.indexOf(parent[index].type) > -1; index--) ;
if(parent[index] && equal(parent[index].type, ‘Identifier‘)){ parent[index].type = ‘FunctionName‘; }
}
function argumentList(parent, p) {
do{
p = assignmentExpression(parent, p) || peek(parent);
if(!equal(p.value, ‘,‘)){ return p; }
p = pushItem(parent, p);
}while(true);
}
function leftHandSideExpression(parent, p) {
p = p? p: peek(parent);
if(equal(p.value, ‘new‘)){ return newExpression(parent, p); }
p = memberExpression(parent, p) || peek(parent);
if(equal(p.value, ‘(‘)){ return callExpression(parent, p); }
return p;
}
function postfixExpression(parent, p) {
p = leftHandSideExpression(parent, p) || peek(parent);
if([‘++‘, ‘--‘].indexOf(p.value) == -1){
var assignmentOperator = [‘=‘, ‘*=‘, ‘/=‘, ‘%=‘, ‘+=‘, ‘-=‘, ‘<<=‘, ‘>>=‘, ‘>>>=‘, ‘&=‘, ‘^=‘, ‘|=‘];
if(assignmentOperator.indexOf(p.value) > -1){
p = pushItem(parent, p);
p = assignmentExpression(parent, p);
}
return p;
}
pushItem(parent, p, p.haveLineTerminator? ‘No line terminator here!‘: ‘‘);
}
function unaryExpression(parent, p) {
var unaryOp = [‘++‘, ‘--‘, ‘+‘, ‘-‘, ‘!‘, ‘~‘, ‘typeof‘, ‘delete‘, ‘void‘];
p = p ? p : peek(parent);
if (unaryOp.indexOf(p.value) > -1) {
p = pushItem(parent, p);
return unaryExpression(parent);
}
else { return postfixExpression(parent, p); }
}
/*function multiplicativeExpression(parent, p){}
function additiveExpression(parent, p){}
function shiftExpression(parent, p){}
function relationalExpression(parent, p){}
function RelationalExpressionNoIn(parent, p){}
function equalityExpression(parent, p){}
function equalityExpressionNoIn(parent, p){}
function bitwiseANDExpression(parent, p){}
function bitwiseANDExpressionNoIn(parent, p){}
function bitwiseXORExpression(parent, p){}
function bitwiseXORExpressionNoIn(parent, p){}
function bitwiseORExpression(parent, p){}
function bitwiseORExpressionNoIn(parent, p){}
function logicalANDExpression(parent, p){}
function logicalANDExpressionNoIn(parent, p){}
function logicalORExpression(parent, p) {}
function logicalORExpressionNoIn(parent, p) {}
function logicalExpression(parent, p) {}
function logicalORExpressionNoIn(parent, p){}*/
function operationExpression(parent, p, noIn) {
var logicalOp = [‘||‘, ‘&&‘, ‘|‘, ‘^‘, ‘&‘, ‘==‘, ‘!=‘, ‘===‘, ‘!==‘, ‘<‘, ‘>‘, ‘<=‘, ‘>=‘, ‘instanceof‘, ‘<<‘, ‘>>‘, ‘>>>‘, ‘+‘, ‘-‘, ‘*‘, ‘/‘, ‘%‘];
if(!noIn){ logicalOp.push(‘in‘); }
do{
p = unaryExpression(parent, p) || peek(parent, p);
if(logicalOp.indexOf(p.value) == -1){ return p; }
p = pushItem(parent, p);
}while(true);
}
function conditionalExpression(parent, p, noIn) {
p = operationExpression(parent, p, noIn) || peek(parent, p);
if(!equal(p.value, ‘?‘)){ return p; }
p = pushItem(parent, p);
p = assignmentExpression(parent, p, noIn) || peek(parent, p);
if(equal(p.value, ‘:‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted :‘; }
return assignmentExpression(parent, p, noIn);
}
//function conditionalExpressionNoIn(parent, p){}
function assignmentExpression(parent, p, noIn) {
return conditionalExpression(parent, p, noIn);
}
//function assignmentExpressionNoIn(parent, p){}
function expression(parent, p, noIn) {
var exp = { type: ‘Expression‘, expressions: [] };
parent.push(exp), parent = exp.expressions;
do{
p = assignmentExpression(parent, p, noIn) || peek(parent);
if(equal(p.value, ‘,‘)){ p = pushItem(parent, p); }
else { return p; }
}while(true);
}
//function expressionNoIn(parent, p){}
}
function Statement(parent, p) {
var _Exp = new Expression();
this.statement = statement;
this.isStatementStart = isStatementStart;
function statement(parent, p) {
p = p ? p : peek(parent);
switch (p.value) {
case ‘{‘: return block(parent, p);
case ‘var‘: return variableStatement(parent, p);
case ‘;‘: return emptyStatement(parent, p);
case ‘if‘: return ifStatement(parent, p);
case ‘do‘:
case ‘while‘:
case ‘for‘: return iterationStatement(parent, p);
case ‘continue‘: return continueStatement(parent, p);
case ‘break‘: return breakStatement(parent, p);
case ‘return‘: return returnStatement(parent, p);
case ‘with‘: return withStatement(parent, p);
case ‘switch‘: return switchStatement(parent, p);
case ‘throw‘: return throwStatement(parent, p);
case ‘try‘: return tryStatement(parent, p);
case ‘debugger‘: return debuggerStatement(parent, p);
//case ‘label‘: return labelStatement(parent, p);
case ‘function‘: return pushItem(parent, p, ‘Invalid statement start!‘);
default:
if(equal(p.type, ‘Identifier‘)){
var currIndex = i, test;
try{
test = peek([]);
}catch(e){
test = {value: ‘not :‘}
}
i = currIndex;
if(equal(test.value, ‘:‘)){ return labelStatement(parent, p); }
}
if(!isExpressionStart(p)){ return p; }
return expressionStatement(parent, p);
}
}
function block(parent, p) {
var b = { type: ‘BlockStatement‘, statement: [] };
parent.push(b), parent = b.statement;
p = p ? p : peek(parent);
if(equal(p.value, ‘{‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted {‘; }
p = p ? p : peek(parent);
if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
p = statementList(parent, p);
if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted }‘; }
return p;
}
function statementList(parent, p) {
do{
p = statement(parent, p);
if(p && !isStatementStart(p)){ return p; }
}while(true);
}
function variableStatement(parent, p) {
var varDeclar = { type: ‘VariableDeclaration‘, declarList: [] };
p = p ? p : peek(parent);
parent.push(varDeclar), parent = varDeclar.declarList;
p = pushItem(parent, p) || peek(parent);
p = variableDeclarationList(parent, p) || peek(parent);
if(!equal(p.value, ‘;‘)){
if(!p.haveLineTerminator && !equal(p.value, ‘}‘)){ p.msg = p.msg || ‘Excepted ;‘; }
}
else{ p = pushItem(parent, p); }
return p;
}
function variableDeclarationList(parent, p, noIn) {
p = p ? p : peek(parent);
do {
p = variableDeclaration(parent, p, noIn) || peek(parent);
if(!equal(p.value, ‘,‘)){
if(equal(p.value, ‘;‘) || p.haveLineTerminator){ return p; }
else if(!equal(p.type, ‘Identifier‘)){ p = pushItem(parent, p, ‘Excepted ,‘); }
else { p.msg = p.msg || ‘Excepted ,‘; }
}
else { p = pushItem(parent, p); }
} while (true);
}
function variableDeclaration(parent, p, noIn) {
p = p ? p : peek(parent);
if (!equal(p.type, ‘Identifier‘)) {
if(!p.msg){
if(equal(p.type, ‘Reservedword‘)){ p.msg = ‘Identifier can not be reservedword!‘; }
else{ p.msg = ‘Excepted identifier!‘ }
}
}
else { p = pushItem(parent, p); }
p = p ? p : peek(parent);
if(!equal(p.value, ‘=‘)){ return p; }
p = pushItem(parent, p);
return _Exp.assignmentExpression(parent, p, noIn);
}
function emptyStatement(parent, p) {
var emptyState = { type: p.type, value: p.value };
parent.push(emptyState);
}
function expressionStatement(parent, p) {
p = _Exp.expression(parent, p) || peek(parent);
if(equal(p.value, ‘;‘)){ return pushItem(parent, p); }
else if(!equal(p.value, ‘}‘) && !p.haveLineTerminator) { p.msg = p.msg || ‘Excepted ;‘; }
return p;
}
function ifStatement(parent, p) {
var ifState = { type: ‘IfStatement‘, statement: [] };
parent.push(ifState), parent = ifState.statement;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘; }
p = _Exp.expression(parent, p) || peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted )‘; }
p = statement(parent, p) || peek(parent);
if(!equal(p.value, ‘else‘)){ return p; }
p = pushItem(parent, p);
return statement(parent);
}
function iterationStatement(parent, p) {
var loop = { type: ‘IterationStatement‘, statement: [] };
parent.push(loop), parent = loop.statement;
pushItem(parent, p);
p = p? p: peek(parent);
if (equal(p.value, ‘do‘)) { return doStatement(parent); }
else if (equal(p.value, ‘while‘)) { return whileStatement(parent); }
else if (equal(p.value, ‘for‘)) { return forStatement(parent); }
else { p.msg = p.msg || ‘Excepted do or while or for!‘; }
return p;
function doStatement(parent, p) {
p = statement(parent, p) || peek(parent);
if(!equal(p.value, ‘while‘)){ p.msg = p.msg || ‘Excepted While!‘; }
else { p = pushItem(parent, p); }
return whileStatement(parent, p);
}
function whileStatement(parent, p) {
p = p? p: peek(parent);
if(!equal(p.value, ‘(‘)){ p.msg = p.msg || ‘Excepted (‘; }
else{ p = pushItem(parent, p); }
p = _Exp.expression(parent, p) || peek(parent);
if(!equal(p.value, ‘)‘)){ p.msg = p.msg || ‘Excepted )‘; }
else{ p = pushItem(parent, p); }
return statement(parent, p);
}
function forStatement(parent, p) {
p = p? p: peek(parent);
if(!equal(p.value, ‘(‘)){ p.msg = p.msg || ‘Excepted (‘; }
else{ p = pushItem(parent, p); }
p = p? p: peek(parent);
if(equal(p.value, ‘var‘)){
p = pushItem(parent, p);
p = variableDeclaration(parent, p, true) || peek(parent);
if(equal(p.value, ‘,‘) || equal(p.value, ‘;‘)){
while(equal(p.value, ‘,‘)){
p = pushItem(parent, p);
p = variableDeclaration(parent, p, true) || peek(parent);
}
}
}
else if(!equal(p.value, ‘;‘)){
var currIndex = i, test;
try{
test = _Exp.leftHandSideExpression([], p) || peek([]);
}catch(e){
test = { value: ‘not in‘ };
}
i = currIndex;
if(equal(test.value, ‘in‘)){ p = _Exp.leftHandSideExpression(parent, p) || peek(parent); }
else { p = _Exp.expression(parent, p, true) || peek(parent); }
}
if(equal(p.value, ‘in‘)){
pushItem(parent, p);
p = _Exp.expression(parent);
}
else if(equal(p.value, ‘;‘)){
p = pushItem(parent, p) || peek(parent);
if(!equal(p.value, ‘;‘)){
p = _Exp.expression(parent, p) || peek(parent);
if(equal(p.value, ‘;‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted ;‘; }
}
else { p = pushItem(parent, p); }
p = p? p: peek(parent);
if(!equal(p.value, ‘)‘)){ p = _Exp.expression(parent, p); }
}
else{ p.msg = p.msg || ‘Excepted ;‘; }
p = p? p: peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted )‘; }
return statement(parent, p);
}
}
function continueStatement(parent, p) {
var con = { type: ‘ContinueStatement‘, statement: [] };
parent.push(con), parent = con.statement;
p = pushItem(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.type, ‘Identifier‘)){ p = pushItem(parent, p); }
p = p? p: peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ p = pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)) { p.msg = p.msg || ‘Excepted ;‘; }
return p;
}
function breakStatement(parent, p) {
var b = { type: ‘BreakStatement‘, statement: [] };
parent.push(b), parent = b.statement;
p = pushItem(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.type, ‘Identifier‘)){ p = pushItem(parent, p); }
p = p? p: peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ p = pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)) { p.msg = p.msg || ‘Excepted ;‘; }
return p;
}
function returnStatement(parent, p) {
var r = { type: ‘ReturnStatement‘, statement: [] };
parent.push(r), parent = r.statement;
p = pushItem(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ p = pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)){
p = _Exp.expression(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ p = pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)){ p.msg = p.msg || ‘Excepted ;‘; }
}
return p;
}
function withStatement(parent, p) {
var w = { type: ‘WhithStatement‘, statement: [] };
parent.push(w), parent = w.statement;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘; }
p = _Exp.expression(parent, p) || peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted )‘; }
return statement(parent, p);
}
function switchStatement(parent, p) {
var s = { type: ‘SwitchStatement‘, statement: [] };
parent.push(s), parent = s.statement;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘; }
p = _Exp.expression(parent, p) || peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted )‘; }
return caseBlock(parent, p);
}
function caseBlock(parent, p) {
var c = { type: ‘CaseStatement‘, statement: [] };
globalVariable.currInstance = ‘caseBlock‘;
parent.push(c), parent = c.statement;
p = p? p: peek(parent);
if(equal(p.value, ‘{‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted {‘; }
p = p? p: peek(parent);
if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
var haveDefault = false;
do{
p = p? p: peek(parent);
if(equal(p.value, ‘case‘)){
pushItem(parent, p);
p = _Exp.expression(parent);
}
else if(equal(p.value, ‘default‘)){
if(haveDefault){ p.msg = p.msg || ‘Repeat default keyword!‘; }
p = pushItem(parent, p);
haveDefault = true;
}
else if(equal(p.value, ‘}‘)){ return pushItem(parent, p); }
p = p? p: peek(parent);
if(equal(p.value, ‘:‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted :‘; }
p = statementList(parent, p);
}while(true);
}
function labelStatement(parent, p) {
var l = { type: ‘LablelStatement‘, statement: [] };
parent.push(l), parent = l.statement;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘:‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted :‘; }
return statement(parent, p);
}
function throwStatement(parent, p) {
var t = { type: ‘ThrowStatement‘, statement: [] };
parent.push(t), parent = t.statement;
p = pushItem(parent, p) || peek(parent);
if(p.haveLineTerminator){ p.msg = p.msg || ‘No lineTerminator here!‘; return p; }
p = _Exp.expression(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ return pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)){ p.msg = p.msg || ‘Excepted ;‘; }
return p;
}
function tryStatement(parent, p) {
var t = { type: ‘TryStatement‘, statement: [] }, haveCatch = false;
parent.push(t), parent = t.statement;
pushItem(parent, p);
p = block(parent) || peek(parent);
if(equal(p.value, ‘catch‘)){
haveCatch = true;
p = pushItem(parent, p);
p = catchStatement(parent, p);
}
p = p? p: peek(parent);
if(equal(p.value, ‘finally‘)){
p = pushItem(parent, p);
p = block(parent, p);
}
else if(!haveCatch) { p.msg = p.msg || ‘Excepted catch or finally!‘; }
return p;
}
function catchStatement(parent, p) {
var c = { type: ‘CatchStatement‘, statement: [] };
parent.push(c), parent = c.statement;
p = pushItem(parent, p) || peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘; }
p = p? p: peek(parent);
if(equal(p.type, ‘Identifier‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Excepted identifier!‘; }
p = p? p: peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted )‘; }
return block(parent, p);
}
function debuggerStatement(parent, p) {
var d = { type: ‘DebuggerStatement‘, statement: [] };
parent.push(d), parent = d.statement;
p = pushItem(parent, p) || peek(parent);
if(p.haveLineTerminator){ return p; }
else if(equal(p.value, ‘;‘)){ return pushItem(parent, p); }
else if(!equal(p.value, ‘}‘)){ p.msg = p.msg || ‘Excepted ;‘; }
return p;
}
function isExpressionStart(p){
var valueArray = [‘this‘, ‘null‘, ‘true‘, ‘false‘, ‘{‘, ‘[‘, ‘(‘, ‘new‘, ‘++‘, ‘--‘, ‘+‘, ‘-‘, ‘!‘, ‘~‘, ‘typeof‘, ‘delete‘, ‘void‘, ‘/‘],
typeArray = [‘Identifier‘, ‘StringLiteral‘, ‘StringLiteral‘, ‘RegularExpressionLiteral‘];
return typeArray.indexOf(p.type) > -1 || valueArray.indexOf(p.value) > -1;
}
function isStatementStart(p){
var keyValue = [‘var‘, ‘return‘, ‘;‘, ‘if‘, ‘for‘, ‘do‘, ‘while‘, ‘switch‘, ‘with‘, ‘debugger‘, ‘continue‘, ‘break‘, ‘throw‘, ‘try‘];
return isExpressionStart(p) || keyValue.indexOf(p.value) > -1;
}
}
function Function(parent, p) {
this.functionExpression = functionExpression;
this.program = program;
this.functionBody = functionBody;
function functionDeclaration(parent, p) {
var f = { type: ‘FunctionDeclaration‘, declaration: [] };
parent.push(f), parent = f.declaration;
p = pushItem(parent, p) || peek(parent);
if(equal(p.type, ‘Identifier‘)){
p.type = ‘FunctionName‘;
p = pushItem(parent, p);
}
else{ p.msg = p.msg || ‘Excepted function name!‘ }
p = p? p: peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘ }
p = p? p: peek(parent);
if(!equal(p.value, ‘)‘)){ p = formalParameterList(parent, p) || peek(parent); }
p = p? p: peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted )‘ }
p = p? p: peek(parent);
if(equal(p.value, ‘{‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted {‘ }
p = functionBody(parent, p) || peek(parent);
if(equal(p.value, ‘}‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted }‘ }
return p;
}
function functionExpression(parent, p) {
var f = { type: ‘FunctionDeclaration‘, declaration: [] };
parent.push(f), parent = f.declaration;
p = pushItem(parent, p) || peek(parent);
if(equal(p.type, ‘Identifier‘)){
p.type = ‘FunctionName‘;
p = pushItem(parent, p);
}
p = p? p: peek(parent);
if(equal(p.value, ‘(‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted (‘ }
p = p? p: peek(parent);
if(!equal(p.value, ‘)‘)){ p = formalParameterList(parent, p) || peek(parent); }
p = p? p: peek(parent);
if(equal(p.value, ‘)‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted )‘ }
p = p? p: peek(parent);
if(equal(p.value, ‘{‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted {‘ }
p = functionBody(parent, p) || peek(parent);
if(equal(p.value, ‘}‘)){ p = pushItem(parent, p); }
else{ p.msg = p.msg || ‘Excepted }‘ }
return p;
}
function formalParameterList(parent, p) {
do{
p = p? p: peek(parent);
if(equal(p.type, ‘Identifier‘)){ p = pushItem(parent, p); }
else { p.msg = p.msg || ‘Function parameter muste be identifier!‘; }
p = p? p: peek(parent);
if(equal(p.value, ‘,‘)){ p = pushItem(parent, p); }
else if(equal(p.value, ‘)‘)){ return p; }
else { p = pushItem(parent, p, ‘Excpted ,‘); }
}while(true);
}
function functionBody(parent, p) {
p = p? p: peek(parent);
if(equal(p.value, ‘}‘)){ return p; }
var f = { type: ‘FunctionBody‘, body: [] }, strictModel = globalVariable.strictModel;
parent.push(f), parent = f.body;
p = p? p: peek(parent);
if(equal(p.value, ‘"use strict"‘) || equal(p.value, "‘use strict‘")){ globalVariable.strictModel = true; }
p = sourceElements(parent, p);
globalVariable.strictModel = strictModel;
return p;
}
function program(parent){
var p = peek(parent);
if(equal(p.value, ‘"use strict"‘) || equal(p.value, "‘use strict‘")){ globalVariable.strictModel = true; }
do{
p = sourceElements(parent, p);
if(p){ p = pushItem(parent, p, ‘Syntax error!‘); }
}while(true);
}
function sourceElements(parent, p) {
do{
p = sourceElement(parent, p);
if(p && !isSourceElementStart(p)){ return p; }
}while(true);
}
function sourceElement(parent, p) {
p = p ? p : peek(parent);
if(equal(p.value, ‘function‘)){ return functionDeclaration(parent, p); }
else { return (new Statement()).statement(parent, p); }
}
function isSourceElementStart(p){
if(equal(p.value, ‘function‘)){ return true; }
else{
var state = new Statement();
return state.isStatementStart(p);
}
}
}
function eol(pos) {
i = pos ? pos : i;
return i >= string.length;
}
function isHex(ch) {
return /[0-9a-fA-F]/g.test(ch);
}
function isSpace(ch) {
return [‘\u0009‘, ‘\u000B‘, ‘\u0009C‘, ‘\u0020‘, ‘\u00A0‘, ‘\uFEFF‘].indexOf(ch) > -1;
}
function isLineTerminator(ch) {
return [‘\u000A‘, ‘\u000D‘, ‘\u2028‘, ‘\u2029‘].indexOf(ch) > -1;
}
function isWhiteSpace(ch){
return isSpace(ch) || isLineTerminator(ch);
}
function isItemInArray(array, item) {
return array.indexOf(item) > -1;
}
function isDigit(ch) {
return /\d/g.test(ch);
}
function isReservedWord(word) {
if (!word) { return false; }
var keyWord = ["break", "do", "instanceof", "pof", "case", "else", "new", "var", "catch", "finally", "return", "void", ‘continue‘, "for",
"switch", "while", "debugger", "function", ‘this‘, ‘with‘, ‘default‘, "if", "throw", "delete", "in", "try"],
nullReserved = [‘null‘],
booleanReserved = [‘true‘, ‘false‘],
futureReserved = ["class", "enum", "extends", "super", "export", "import"],
strictReserved = ["implements", ‘let‘, "private", "public", "interface", "package", "protected", "static", ‘yield‘],
reserved = keyWord.concat(nullReserved).concat(futureReserved).concat(booleanReserved),
strictModel = globalVariable.strictModel;
if (strictModel) { reserved = reserved.concat(strictReserved); }
return reserved.indexOf(word) > -1;
}
function equal(e1, e2, deep) {
return deep ? e1 === e2 : e1 == e2;
}
function pushItem(parent, p, msg) {
if (!p || !p.value) { return; }
var o = { type: p.type, value: p.value };
if (p.msg || msg) {
o.type = ‘Exception‘;
o.msg = p.msg || msg;
}
parent.push(o);
}
}
</script>
</body>
</html>