editor/src/parsejs.cpp

788 lines
40 KiB
C++

/*******************************************
* Zira Editor
* A lightweight PHP Editor
* (C)2019 https://github.com/ziracms/editor
*******************************************/
#include "parsejs.h"
#include <QVector>
#include "helper.h"
const int EXPECT_FUNCTION = 0;
const int EXPECT_VARIABLE = 1;
const int EXPECT_CONST = 2;
const int EXPECT_CONST_VALUE = 3;
const int EXPECT_CLASS_ES6 = 4;
const int EXPECT_CLASS_ES6_EXTENDED = 5;
ParseJS::ParseJS()
{
commentSLExpression = QRegularExpression("[/][/]([^\n]+?)(?:[\n]|$)", QRegularExpression::DotMatchesEverythingOption);
regexpExpression = QRegularExpression("(?:[^\\sa-zA-Z0-9_\\$\\)\\]<\\*\\~\\\\][\\s]*)[/](.*?[^\\\\])[/]", QRegularExpression::DotMatchesEverythingOption);
parseExpression = QRegularExpression("([a-zA-Z0-9_\\$]+|[\\(\\)\\{\\}\\[\\]\\.,=;:!@#%^&*\\-+/\\|<>\\?\\\\])", QRegularExpression::DotMatchesEverythingOption);
nameExpression = QRegularExpression("^[a-zA-Z_\\$][a-zA-Z0-9_\\$]*$");
}
QString ParseJS::cleanUp(QString text)
{
comments.clear();
prepare(text);
// strip strings & comments
int offset = 0;
QList<int> matchesPos;
QRegularExpressionMatch stringDQMatch;
QRegularExpressionMatch stringSQMatch;
QRegularExpressionMatch commentMLMatch;
QRegularExpressionMatch commentSLMatch;
QRegularExpressionMatch regexpMatch;
QRegularExpressionMatch backtickMatch;
int stringDQPos = -2,
stringSQPos = -2,
commentMLPos = -2,
commentSLPos = -2,
regexpPos = -2,
backtickPos = -2;
do {
matchesPos.clear();
if (stringDQPos != -1 && stringDQPos < offset) {
stringDQMatch = stringDQExpression.match(text, offset);
stringDQPos = stringDQMatch.capturedStart(1)-1;
}
if (stringDQPos >= 0) matchesPos.append(stringDQPos);
if (stringSQPos != -1 && stringSQPos < offset) {
stringSQMatch = stringSQExpression.match(text, offset);
stringSQPos = stringSQMatch.capturedStart(1)-1;
}
if (stringSQPos >= 0) matchesPos.append(stringSQPos);
if (commentMLPos != -1 && commentMLPos < offset) {
commentMLMatch = commentMLExpression.match(text, offset);
commentMLPos = commentMLMatch.capturedStart();
}
if (commentMLPos >= 0) matchesPos.append(commentMLPos);
if (commentSLPos != -1 && commentSLPos < offset) {
commentSLMatch = commentSLExpression.match(text, offset);
commentSLPos = commentSLMatch.capturedStart();
}
if (commentSLPos >= 0) matchesPos.append(commentSLPos);
if (regexpPos != -1 && regexpPos < offset) {
regexpMatch = regexpExpression.match(text, offset);
regexpPos = regexpMatch.capturedStart(1)-1;
}
if (regexpPos >= 0) matchesPos.append(regexpPos);
if (backtickPos != -1 && backtickPos < offset) {
backtickMatch = backtickExpression.match(text, offset);
backtickPos = backtickMatch.capturedStart(1)-1;
}
if (backtickPos >= 0) matchesPos.append(backtickPos);
if (matchesPos.size() == 0) break;
std::sort(matchesPos.begin(), matchesPos.end());
int pos = matchesPos.at(0);
if (stringDQPos == pos) {
offset = stringDQMatch.capturedStart() + stringDQMatch.capturedLength();
strip(stringDQMatch, text, 1);
continue;
}
if (stringSQPos == pos) {
offset = stringSQMatch.capturedStart() + stringSQMatch.capturedLength();
strip(stringSQMatch, text, 1);
continue;
}
if (commentMLPos == pos) {
offset = commentMLMatch.capturedStart() + commentMLMatch.capturedLength();
QString stripped = strip(commentMLMatch, text, 0); // group 0
comments[getLine(text, offset)] = stripped.toStdString();
continue;
}
if (commentSLPos == pos) {
offset = commentSLMatch.capturedStart(1) + commentSLMatch.capturedLength(1);
QString stripped = strip(commentSLMatch, text, 0); // group 0
comments[getLine(text, offset)] = stripped.toStdString();
continue;
}
if (regexpPos == pos) {
offset = regexpMatch.capturedStart(1) + regexpMatch.capturedLength(1);
strip(regexpMatch, text, 1);
continue;
}
if (backtickPos == pos) {
offset = backtickMatch.capturedStart() + backtickMatch.capturedLength();
strip(backtickMatch, text, 1);
continue;
}
} while (matchesPos.size() > 0);
return text;
}
bool ParseJS::isValidName(QString name)
{
QRegularExpressionMatch m = nameExpression.match(name);
return (m.capturedStart()==0);
}
void ParseJS::addClass(QString name, int line) {
if (!isValidName(name)) return;
ParseResultClass cls;
cls.name = name;
cls.line = line;
result.classes.append(cls);
classIndexes[name.toStdString()] = result.classes.size() - 1;
}
void ParseJS::addFunction(QString clsName, QString name, QString args, int minArgs, int maxArgs, bool isGlobal, QString returnType, QString comment, int line) {
if (!isValidName(name)) return;
ParseResultFunction func;
func.name = name;
func.clsName = clsName;
func.args = args;
func.minArgs = minArgs;
func.maxArgs = maxArgs;
func.isGlobal = isGlobal;
func.returnType = returnType;
func.comment = comment;
func.line = line;
result.functions.append(func);
functionIndexes[clsName.toStdString() + "::" + name.toStdString()] = result.functions.size() - 1;
if (clsName.size() > 0) {
classIndexesIterator = classIndexes.find(clsName.toStdString());
if (classIndexesIterator != classIndexes.end()) {
int i = classIndexesIterator->second;
if (result.classes.size() > i) {
ParseJS::ParseResultClass cls = result.classes.at(i);
cls.functionIndexes.append(result.functions.size() - 1);
result.classes.replace(i, cls);
}
}
}
}
void ParseJS::updateFunctionReturnType(QString clsName, QString funcName, QString returnType)
{
functionIndexesIterator = functionIndexes.find(clsName.toStdString() + "::" + funcName.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseResultFunction func = result.functions.at(i);
func.returnType = returnType;
result.functions.replace(i, func);
}
}
}
void ParseJS::addVariable(QString clsName, QString funcName, QString name, QString type, int line)
{
if (!isValidName(name)) return;
variableIndexesIterator = variableIndexes.find(clsName.toStdString() + "::" + funcName.toStdString() + "::" + name.toStdString());
if (variableIndexesIterator != variableIndexes.end()) return;
ParseResultVariable variable;
variable.name = name;
variable.clsName = clsName;
variable.funcName = funcName;
variable.type = type;
variable.line = line;
result.variables.append(variable);
variableIndexes[clsName.toStdString() + "::" + funcName.toStdString() + "::" + name.toStdString()] = result.variables.size() - 1;
if (clsName.size() > 0 && funcName.size() == 0) {
classIndexesIterator = classIndexes.find(clsName.toStdString());
if (classIndexesIterator != classIndexes.end()) {
int i = classIndexesIterator->second;
if (result.classes.size() > i) {
ParseJS::ParseResultClass cls = result.classes.at(i);
cls.variableIndexes.append(result.variables.size() - 1);
result.classes.replace(i, cls);
}
}
} else if (funcName.size() > 0) {
functionIndexesIterator = functionIndexes.find(clsName.toStdString() + "::" + funcName.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseJS::ParseResultFunction func = result.functions.at(i);
func.variableIndexes.append(result.variables.size() - 1);
result.functions.replace(i, func);
}
}
}
}
void ParseJS::updateVariableType(QString clsName, QString funcName, QString varName, QString type)
{
variableIndexesIterator = variableIndexes.find(clsName.toStdString() + "::" + funcName.toStdString() + "::" + varName.toStdString());
if (variableIndexesIterator != variableIndexes.end()) {
int i = variableIndexesIterator->second;
if (result.variables.size() > i) {
ParseResultVariable variable = result.variables.at(i);
variable.type = type;
result.variables.replace(i, variable);
}
}
}
void ParseJS::addConstant(QString clsName, QString funcName, QString name, QString value, int line)
{
if (!isValidName(name)) return;
ParseResultConstant constant;
constant.name = name;
constant.clsName = clsName;
constant.funcName = funcName;
constant.value = value;
constant.line = line;
result.constants.append(constant);
constantIndexes[clsName.toStdString() + "::" + funcName.toStdString() + "::" + name.toStdString()] = result.constants.size() - 1;
if (funcName.size() > 0) {
functionIndexesIterator = functionIndexes.find(clsName.toStdString()+"::"+funcName.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseJS::ParseResultFunction func = result.functions.at(i);
func.constantIndexes.append(result.constants.size() - 1);
result.functions.replace(i, func);
}
}
}
}
void ParseJS::addComment(QString text, int line) {
QString name = "";
if (text.size() > 0) {
if (text.indexOf("//")==0) text = text.mid(2);
else text.replace(QRegularExpression("^[/][*](.*)[*][/]$", QRegularExpression::DotMatchesEverythingOption), "\\1");
QString text_clean = "";
QStringList textList = text.split("\n");
for (int i=0; i<textList.size(); i++) {
QString text_line = textList.at(i).trimmed().replace(QRegularExpression("^[*]+[\\s]*"), "").replace(QRegularExpression("[\\s]*[*]+$"), "");
if (text_line.size() == 0) continue;
if (text_clean.size() > 0) text_clean += "\n";
if (name.size() == 0) name = text_line;
text_clean += text_line;
}
text = text_clean;
}
if (text.size() == 0 || name.size() == 0) return;
ParseResultComment comment;
comment.name = name;
comment.text = text;
comment.line = line;
result.comments.append(comment);
}
void ParseJS::addError(QString text, int line, int symbol) {
ParseResultError error;
error.text = text;
error.line = line;
error.symbol = symbol;
result.errors.append(error);
}
void ParseJS::parseCode(QString & code, QString & origText)
{
// parse data
QString current_class = "";
QString current_function = "";
QString current_function_args = "";
int current_function_min_args = 0;
int current_function_max_args = 0;
QString current_function_return_type = "";
QString current_variable = "";
QString current_variable_type = "";
QString current_constant = "";
QString current_constant_value = "";
QString current_class_es6 = "";
QString current_class_es6_parent = "";
QString expected_function_name = "";
QStringList expected_function_args;
int scope = 0;
int functionScope = -1, classES6Scope = -1;
int pars = 0;
int curlyBrackets = 0, roundBrackets = 0, squareBrackets = 0;
QVector<int> curlyBracketsList, roundBracketsList, squareBracketsList;
int functionArgPars = -1;
int functionArgsStart = -1;
int constantValueStart = -1;
bool functionParsFound = false, classES6ParsFound = false;
int expect = -1;
QString expectName = "";
QString prevK = "", prevPrevK = "", prevPrevPrevK = "", prevPrevPrevPrevK = "", prevPrevPrevPrevPrevK = "", prevPrevPrevPrevPrevPrevK = "", prevPrevPrevPrevPrevPrevPrevK = "", prevPrevPrevPrevPrevPrevPrevPrevK = "", prevPrevPrevPrevPrevPrevPrevPrevPrevK = "";
int functionStart = -1, variableStart = -1, constantStart = -1, classES6Start = -1;
QString class_variable = "";
QString expected_class_es6_name = "";
QRegularExpressionMatchIterator mi = parseExpression.globalMatch(code);
while(mi.hasNext()){
QRegularExpressionMatch m = mi.next();
if (m.capturedStart(1) < 0) continue;
QString k = m.captured(1).trimmed();
if (k.size() == 0) continue;
// classes ES6
if ((prevPrevK.size() == 0 || prevPrevK == ";" || prevPrevK == "{" || prevPrevK == "}" || prevPrevK == "var" || prevPrevK == "let" || prevPrevK == "const" || prevPrevK == "final") && prevK.size() > 0 && k == "=" && functionArgsStart < 0 && ((prevPrevK != "var" && prevPrevK != "let" && prevPrevK != "const" && prevPrevK != "final") || (current_function.size() == 0 && scope == 0) || (functionScope >= 0 && functionScope == scope - 1))) {
expected_class_es6_name = prevK;
}
if ((expect < 0 || expect == EXPECT_VARIABLE) && k.toLower() == "class" && (prevK == ";" || prevK == "{" || prevK == "}" || prevK == "=" || prevK == "final" || prevK.size() == 0) && current_function.size() == 0) {
expect = EXPECT_CLASS_ES6;
expectName = "";
current_class_es6_parent = "";
classES6Start = m.capturedStart(1);
classES6ParsFound = false;
} else if (expect == EXPECT_CLASS_ES6 && expectName.size() == 0 && k != "{" && !classES6ParsFound) {
if (k != "(" && k != ")" && k != "{" && k != "extends" && current_class_es6.size() == 0) {
expectName = k;
} else {
classES6ParsFound = true;
}
} else if (expect == EXPECT_CLASS_ES6 && expectName.size() > 0 && k.toLower() == "extends") {
expect = EXPECT_CLASS_ES6_EXTENDED;
} else if (expect == EXPECT_CLASS_ES6_EXTENDED && expectName.size() > 0 && current_class_es6_parent.size() == 0 && k != "{") {
current_class_es6_parent = k;
} else if ((expect == EXPECT_CLASS_ES6 || expect == EXPECT_CLASS_ES6_EXTENDED) && (expectName.size() > 0 || expected_class_es6_name.size() > 0) && k == "{") {
current_class_es6 = expectName.size() > 0 ? expectName : expected_class_es6_name;
int line = 0;
if (classES6Start >= 0) line = getLine(origText, classES6Start);
addClass(current_class_es6, line);
classES6Scope = scope;
expect = -1;
expectName = "";
expected_class_es6_name = "";
classES6ParsFound = false;
expected_function_name = "";
}
// classes
if (prevPrevPrevPrevPrevK.size() > 0 && prevPrevPrevPrevK == "." && prevPrevPrevK == "prototype" && prevPrevK == "." && prevK.size() > 0 && k == "=" && functionArgsStart < 0) {
QString clsName = prevPrevPrevPrevPrevK;
functionIndexesIterator = functionIndexes.find("::"+clsName.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
current_class = clsName;
ParseJS::ParseResultFunction func = result.functions.at(i);
classIndexesIterator = classIndexes.find(clsName.toStdString());
if (classIndexesIterator == classIndexes.end()) {
addClass(clsName, func.line);
}
}
}
}
// functions
if ((prevPrevK.size() == 0 || prevPrevK == ";" || prevPrevK == "{" || prevPrevK == "}" || prevPrevK == "var" || prevPrevK == "let" || prevPrevK == "const" || prevPrevK == "final" || (prevPrevK == "." && prevPrevPrevK == "prototype" && prevPrevPrevPrevK == ".")) && prevK.size() > 0 && k == "=" && functionArgsStart < 0 && ((prevPrevK != "var" && prevPrevK != "let" && prevPrevK != "const" && prevPrevK != "final") || (current_function.size() == 0 && scope == 0) || (functionScope >= 0 && functionScope == scope - 1))) {
expected_function_name = prevK;
}
if (expect != EXPECT_CLASS_ES6 && expect != EXPECT_CLASS_ES6_EXTENDED && current_class_es6.size() > 0 && classES6Scope == scope - 1 && k.size() > 0 && k != "(" && k != ")" && k != "{" && k != "}" && current_function.size() == 0 && functionArgPars < 0) {
expect = EXPECT_FUNCTION;
current_function_args = "";
expected_function_args.clear();
current_function_min_args = 0;
current_function_max_args = 0;
current_function_return_type = "";
expectName = "";
functionArgPars = -1;
functionArgsStart = -1;
functionParsFound = false;
functionStart = m.capturedStart(1);
expectName = k;
} else if ((expect < 0 || expect == EXPECT_VARIABLE) && current_function.size() == 0 && k == "function" && functionArgsStart < 0) {
expect = EXPECT_FUNCTION;
current_function_args = "";
expected_function_args.clear();
current_function_min_args = 0;
current_function_max_args = 0;
current_function_return_type = "";
expectName = "";
functionArgPars = -1;
functionArgsStart = -1;
functionParsFound = false;
functionStart = m.capturedStart(1);
} else if (expect == EXPECT_FUNCTION && expectName.size() == 0 && k != "(" && k != ")" && k != "{" && functionArgsStart < 0 && current_function_args.size() == 0 && !functionParsFound) {
expectName = k;
} else if (expect == EXPECT_FUNCTION && functionArgPars < 0 && k == "(") {
functionArgPars = pars;
functionArgsStart = m.capturedStart(1);
functionParsFound = true;
} else if (expect == EXPECT_FUNCTION && (expectName.size() > 0 || expected_function_name.size() > 0) && k == "{" && functionArgsStart < 0) {
current_function = expectName.size() > 0 ? expectName : expected_function_name;
int line = 0;
if (functionStart >= 0) line = getLine(origText, functionStart);
QString current_comment = "";
int comment_line = getFirstNotEmptyLineTo(origText, functionStart);
if (comment_line > 0) {
commentsIterator = comments.find(comment_line);
if (commentsIterator != comments.end()) {
current_comment = QString::fromStdString(commentsIterator->second);
}
}
if (current_comment.size() > 0) {
if (current_comment.indexOf("//")==0) current_comment = current_comment.mid(2);
else current_comment.replace(QRegularExpression("^[/][*](.*)[*][/]$", QRegularExpression::DotMatchesEverythingOption), "\\1");
QString current_comment_clean = "";
QStringList commentList = current_comment.split("\n");
for (int i=0; i<commentList.size(); i++) {
//QString current_comment_line = commentList.at(i).trimmed().replace(QRegularExpression("^[*]+[\\s]*"), "").replace(QRegularExpression("^[@]"), "- ");
QString current_comment_line = commentList.at(i).trimmed().replace(QRegularExpression("^[*]+[\\s]*"), "");
if (current_comment_line.size() == 0) continue;
if (current_comment_clean.size() > 0) current_comment_clean += "\n";
current_comment_clean += current_comment_line;
}
current_comment = current_comment_clean;
}
bool isGlobal = (scope == 0);
QString cls = current_class_es6.size() > 0 ? current_class_es6 : current_class;
addFunction(cls, current_function, current_function_args, current_function_min_args, current_function_max_args, isGlobal, current_function_return_type, current_comment, line);
if (expected_function_args.size() > 0) {
for (int i=0; i<expected_function_args.size(); i++) {
QString argName = expected_function_args.at(i);
QString cls = current_class_es6.size() > 0 ? current_class_es6 : current_class;
addVariable(cls, current_function, argName, "", line);
}
}
expect = -1;
expectName = "";
functionScope = scope;
functionArgPars = -1;
functionArgsStart = -1;
expected_function_name = "";
functionParsFound = false;
expected_class_es6_name = "";
}
// class method return "this" type
if (current_function.size() > 0 && current_class.size() > 0 && current_function_return_type.size() == 0 && prevPrevK == "return" && prevK == "this" && k == ";" && functionScope == scope - 1) {
current_function_return_type = current_class;
updateFunctionReturnType(current_class, current_function, current_function_return_type);
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && prevPrevK == "return" && prevK.size() > 0 && k == ";" && functionScope == scope - 1) {
// function return "var" type
variableIndexesIterator = variableIndexes.find(current_class.toStdString() + "::" + current_function.toStdString() + "::" + prevK.toStdString());
if (variableIndexesIterator != variableIndexes.end()) {
int i = variableIndexesIterator->second;
if (result.variables.size() > i) {
ParseResultVariable variable = result.variables.at(i);
if (variable.type.size() > 0) {
current_function_return_type = variable.type;
updateFunctionReturnType(current_class, current_function, current_function_return_type);
}
}
}
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && prevPrevPrevPrevK == "return" && prevPrevPrevK.size() > 0 && prevPrevK == "(" && prevK == ")" && k == ";" && functionScope == scope - 1) {
// function return "function()" type
functionIndexesIterator = functionIndexes.find("::" + prevPrevPrevK.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseResultFunction func = result.functions.at(i);
if (func.returnType.size() > 0) {
current_function_return_type = func.returnType;
updateFunctionReturnType(current_class, current_function, current_function_return_type);
}
}
}
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && prevPrevPrevPrevPrevK == "return" && prevPrevPrevPrevK == "new" && prevPrevPrevK.size() > 0 && prevPrevK == "(" && prevK == ")" && k == ";" && functionScope == scope - 1) {
// function return "new Class()" type
QString type = prevPrevPrevK;
updateFunctionReturnType(current_class, current_function, type);
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && prevPrevPrevK == "return" && prevPrevK == "new" && prevK.size() > 0 && k == ";" && functionScope == scope - 1) {
// function return "new Class" type
QString type = prevK;
updateFunctionReturnType(current_class, current_function, type);
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && current_class.size() > 0 && prevPrevPrevPrevK == "return" && prevPrevPrevK == "this" && prevPrevK == "." && prevK.size() > 0 && k == ";" && functionScope == scope - 1) {
// function return "this.var" type
variableIndexesIterator = variableIndexes.find(current_class.toStdString() + "::" + "::" + prevK.toStdString());
if (variableIndexesIterator != variableIndexes.end()) {
int i = variableIndexesIterator->second;
if (result.variables.size() > i) {
ParseResultVariable variable = result.variables.at(i);
if (variable.type.size() > 0) {
current_function_return_type = variable.type;
updateFunctionReturnType(current_class, current_function, current_function_return_type);
}
}
}
} else if (current_function.size() > 0 && current_function_return_type.size() == 0 && current_class.size() > 0 && prevPrevPrevPrevPrevPrevK == "return" && prevPrevPrevPrevPrevK == "this" && prevPrevPrevPrevK == "." && prevPrevPrevK.size() > 0 && prevPrevK == "(" && prevK == ")" && k == ";" && functionScope == scope - 1) {
// function return "this.function()" type
functionIndexesIterator = functionIndexes.find(current_class.toStdString() + "::" + prevPrevPrevK.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseResultFunction func = result.functions.at(i);
if (func.returnType.size() > 0) {
current_function_return_type = func.returnType;
updateFunctionReturnType(current_class, current_function, current_function_return_type);
}
}
}
}
// variables
if (expect < 0 && functionArgPars < 0 && k.size() > 0 && (prevK == "var" || prevK == "let" || prevK == "const" || prevK.size() > 1 || (prevK.size() == 1 && ((prevK[0]).isLetter() || prevK[0] == '$'))) && ((current_function.size() == 0 && scope == 0) || (functionScope >= 0 && functionScope == scope - 1))) {
expect = EXPECT_VARIABLE;
expectName = k;
current_variable = "";
current_variable_type = "";
class_variable = "";
variableStart = m.capturedStart(1);
} else if (expect == EXPECT_VARIABLE && expectName.size() > 0 && current_variable.size() == 0 && prevK == "=" && k.size() > 0 && (k != "function" || current_function.size() > 0) && (k != "class" || current_function.size() > 0)) {
current_variable = expectName;
int line = 0;
if (variableStart >= 0) line = getLine(origText, variableStart);
QString cls = current_class_es6.size() > 0 ? current_class_es6 : current_class;
addVariable(cls, current_function, current_variable, current_variable_type, line);
expect = -1;
expectName = "";
class_variable = "";
} else if (functionScope >= 0 && functionScope == scope - 1 && functionArgPars < 0 && current_function.size() > 0 && prevK == "function" && k.size() > 0 && k != "(" && k != ")" && k != "{") {
QString cls = current_class_es6.size() > 0 ? current_class_es6 : current_class;
addVariable(cls, current_function, k, "", getLine(origText, m.capturedStart(1)));
} else if (functionScope >= 0 && functionScope == scope - 1 && functionArgPars < 0 && current_function.size() > 0 && prevK == "class" && k.size() > 0 && k != "(" && k != ")" && k != "{") {
QString cls = current_class_es6.size() > 0 ? current_class_es6 : current_class;
addVariable(cls, current_function, k, "", getLine(origText, m.capturedStart(1)));
} else if (scope == 0 && (prevPrevPrevK.size() == 0 || prevPrevPrevK == ";" || prevPrevPrevK == "{" || prevPrevPrevK == "}") && prevPrevK.size() > 0 && prevK == "=" && k.size() > 0 && k != "function" && k != "class") {
current_variable = prevPrevK;
int line = getLine(origText, m.capturedStart(1));
addVariable("", "", current_variable, "", line);
expect = -1;
expectName = "";
class_variable = "";
}
// class property
if (class_variable.size() == 0 && functionArgPars < 0 && k == "=" && prevK.size() > 0 && prevPrevK == "." && prevPrevPrevK == "this" && current_class.size() == 0 && current_function.size() > 0 && functionArgsStart < 0 && functionScope == scope - 1 && current_class_es6.size() == 0) {
variableIndexesIterator = variableIndexes.find(current_function.toStdString() + "::" + "::" + prevK.toStdString());
if (variableIndexesIterator == variableIndexes.end()) {
classIndexesIterator = classIndexes.find(current_function.toStdString());
if (classIndexesIterator == classIndexes.end()) {
functionIndexesIterator = functionIndexes.find("::"+current_function.toStdString());
if (functionIndexesIterator != functionIndexes.end()) {
int i = functionIndexesIterator->second;
if (result.functions.size() > i) {
ParseJS::ParseResultFunction func = result.functions.at(i);
addClass(current_function, func.line);
}
}
}
class_variable = prevK;
current_variable = "";
current_variable_type = "";
addVariable(current_function, "", class_variable, "", getLine(origText, m.capturedStart(1)));
}
} else if (class_variable.size() == 0 && functionArgPars < 0 && k == "=" && prevK.size() > 0 && prevPrevK == "." && prevPrevPrevK == "this" && current_class_es6.size() > 0 && current_function.size() > 0 && current_function == "constructor" && functionArgsStart < 0 && functionScope == scope - 1) {
variableIndexesIterator = variableIndexes.find(current_class_es6.toStdString() + "::" + "::" + prevK.toStdString());
if (variableIndexesIterator == variableIndexes.end()) {
class_variable = prevK;
current_variable = "";
current_variable_type = "";
addVariable(current_class_es6, "", class_variable, "", getLine(origText, m.capturedStart(1)));
}
}
// variable type
if (current_variable.size() > 0 && current_variable_type.size() == 0 && prevPrevK == "=" && prevK == "new" && ((current_function.size() == 0 && scope == 0) || (functionScope >= 0 && functionScope == scope - 1))) {
current_variable_type = k;
updateVariableType(current_class, current_function, current_variable, current_variable_type);
} else if (class_variable.size() > 0 && current_class.size() == 0 && current_function.size() > 0 && prevPrevK == "=" && prevK == "new" && functionScope == scope - 1) {
// class property type
QString class_variable_type = k;
updateVariableType(current_function, "", class_variable, class_variable_type);
}
// constants
if (expect < 0 && prevK == "const" && ((current_function.size() == 0 && scope == 0) || (functionScope >= 0 && functionScope == scope - 1)) && functionArgsStart < 0) {
expect = EXPECT_CONST;
expectName = k;
current_constant = "";
current_constant_value = "";
constantValueStart = -1;
constantStart = m.capturedStart(1);
} else if (expect == EXPECT_CONST && expectName.size() > 0 && k == "=") {
expect = EXPECT_CONST_VALUE;
constantValueStart = m.capturedStart(1) + 1;
} else if (expect == EXPECT_CONST_VALUE && expectName.size() > 0 && k == ";") {
current_constant = expectName;
current_constant_value = origText.mid(constantValueStart, m.capturedStart(1)-constantValueStart).trimmed();
int line = 0;
if (constantStart >= 0) line = getLine(origText, constantStart);
addConstant(current_class, current_function, current_constant, current_constant_value, line);
expect = -1;
expectName = "";
constantValueStart = -1;
}
if ((expect == EXPECT_VARIABLE || expect == EXPECT_CONST || class_variable.size() > 0) && (k == "-" || k == "+" || k == "*" || k == "/" || k == "%" || k == "&" || k == "|" || k == ":" || k == ">" || k == "<" || k == "?" || k == "[" || k == "]" || k == "(" || k == ")" || k == ".")) {
expect = -1;
expectName = "";
class_variable = "";
}
if ((k == ";" || k == "{" || k == "}") && functionArgsStart < 0) {
expect = -1;
expectName = "";
class_variable = "";
expected_function_name = "";
expected_class_es6_name = "";
}
// braces
if (k == "{") {
scope++;
curlyBrackets++;
curlyBracketsList.append(m.capturedStart(1)+1);
}
if (k == "}") {
scope--;
if (scope < 0) scope = 0;
curlyBrackets--;
curlyBracketsList.append(-1 * (m.capturedStart(1)+1));
// class ES6 close
if (current_class_es6.size() > 0 && classES6Scope >= 0 && classES6Scope == scope) {
current_class_es6 = "";
current_class_es6_parent = "";
classES6Scope = -1;
classES6Start = -1;
expected_class_es6_name = "";
classES6ParsFound = false;
expected_function_name = "";
}
// function close
if (current_function.size() > 0 && functionScope >= 0 && functionScope == scope) {
current_function = "";
current_function_args = "";
current_function_min_args = 0;
current_function_max_args = 0;
current_function_return_type = "";
expected_function_args.clear();
functionScope = -1;
functionArgPars = -1;
functionArgsStart = -1;
functionStart = -1;
expected_function_name = "";
current_class = "";
functionParsFound = false;
expected_class_es6_name = "";
}
}
// parens
if (k == "(") {
pars++;
roundBrackets++;
roundBracketsList.append(m.capturedStart(1)+1);
}
if (k == ")") {
pars--;
if (pars < 0) pars = 0;
roundBrackets--;
roundBracketsList.append(-1 * (m.capturedStart(1)+1));
// function args
if (functionArgPars >= 0 && functionArgPars == pars && functionArgsStart >= 0) {
current_function_args = origText.mid(functionArgsStart+1, m.capturedStart(1)-functionArgsStart-1).trimmed();
current_function_args = Helper::stripScopedText(current_function_args);
if (current_function_args.size() > 0) {
QString current_function_args_cleaned = "";
QStringList argsList, argsDefaultsList;
QString argName, argDefault;
argsList = current_function_args.split(",");
for (int i=0; i<argsList.size(); i++) {
argName = ""; argDefault = "";
argsDefaultsList = argsList.at(i).trimmed().split("=");
if (argsDefaultsList.size() == 2) {
argDefault = argsDefaultsList.at(1).trimmed();
} else if (argsDefaultsList.size() > 2) {
continue;
}
argName = argsDefaultsList.at(0).trimmed();
if (argName.indexOf(" ") >= 0) {
argName = argName.mid(argName.lastIndexOf(" ")+1);
}
if (argName.size() == 0) {
continue;
}
current_function_max_args++;
if (argDefault.size() == 0) {
current_function_min_args++;
}
expected_function_args.append(argName);
if (current_function_args_cleaned.size() > 0) {
current_function_args_cleaned += ", ";
}
if (argDefault.size() > 0) argDefault = " = "+argDefault;
current_function_args_cleaned += argName + argDefault;
}
current_function_args = current_function_args_cleaned;
}
functionArgPars = -1;
functionArgsStart = -1;
}
}
// brackets
if (k == "[") {
squareBrackets++;
squareBracketsList.append(m.capturedStart(1)+1);
}
if (k == "]") {
squareBrackets--;
squareBracketsList.append(-1 * (m.capturedStart(1)+1));
}
prevPrevPrevPrevPrevPrevPrevPrevPrevK = prevPrevPrevPrevPrevPrevPrevPrevK;
prevPrevPrevPrevPrevPrevPrevPrevK = prevPrevPrevPrevPrevPrevPrevK;
prevPrevPrevPrevPrevPrevPrevK = prevPrevPrevPrevPrevPrevK;
prevPrevPrevPrevPrevPrevK = prevPrevPrevPrevPrevK;
prevPrevPrevPrevPrevK = prevPrevPrevPrevK;
prevPrevPrevPrevK = prevPrevPrevK;
prevPrevPrevK = prevPrevK;
prevPrevK = prevK;
prevK = k;
}
if (curlyBrackets > 0) {
int offset = findOpenScope(curlyBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Unclosed brace"), line, offset);
} else if (curlyBrackets < 0) {
int offset = findCloseScope(curlyBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Excess brace"), line, offset);
}
if (roundBrackets > 0) {
int offset = findOpenScope(roundBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Unclosed parenthesis"), line, offset);
} else if (roundBrackets < 0) {
int offset = findCloseScope(roundBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Excess parenthesis"), line, offset);
}
if (squareBrackets > 0) {
int offset = findOpenScope(squareBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Unclosed bracket"), line, offset);
} else if (squareBrackets < 0) {
int offset = findCloseScope(squareBracketsList);
if (offset != 0) offset = abs(offset) - 1;
int line = getLine(origText, offset);
addError(QObject::tr("Excess bracket"), line, offset);
}
}
void ParseJS::reset()
{
functionIndexes.clear();
variableIndexes.clear();
constantIndexes.clear();
classIndexes.clear();
comments.clear();
}
ParseJS::ParseResult ParseJS::parse(QString text)
{
result = ParseResult();
reset();
QString cleanText = cleanUp(text);
parseCode(cleanText, text);
// comments
std::map<int, std::string> orderedComments(comments.begin(), comments.end());
for (auto & commentsIterator : orderedComments) {
addComment(QString::fromStdString(commentsIterator.second), commentsIterator.first);
}
return result;
}