Commit ece94fa2 authored by Victor Yacovlev's avatar Victor Yacovlev

Template modules initial support

REQUIRES AUTOMATIC TESTING!
parent 881da65d
......@@ -248,6 +248,7 @@ class Name:
"""
return str(self.data["ascii"])
@property
def cppValue(self):
"""
Returns a valid C++ name based on source name
......@@ -285,6 +286,8 @@ class Name:
for c in ascii:
if c == ' ':
nextIsCap = True
elif c in "\\%":
break
else:
if nextIsCap:
result += c.upper()
......@@ -301,7 +304,7 @@ class Name:
:rtype: str
:return: valid C++ identifier
"""
cpp = self.cppValue()
cpp = self.cppValue
return cpp[0].upper() + cpp[1:]
......@@ -438,7 +441,7 @@ class BaseType:
assert isinstance(baseType, BaseType)
fieldsDecl += " "
fieldsDecl += baseType.qtName() + " "
fieldsDecl += name.cppValue() + ";"
fieldsDecl += name.cppValue + ";"
fieldsDecl += "\n"
return _renderTemplate("""
struct $name {
......@@ -555,7 +558,7 @@ $fields
defvalue = "0"
elif typee == "bool":
defvalue = "false"
field = fieldName.cppValue()
field = fieldName.cppValue
bodyEncode += " result << QVariant(record.%s);\n" % field
bodyDecode += " result.%s = alist.size() > %i ? alist.at(%i).%s : %s;\n" % (
field, index, index, conversion, defvalue
......@@ -650,7 +653,7 @@ class Argument:
result += self.baseType.qtName()
if self.dimension > 0 or not self.baseType.qtName() in ["int", "qreal", "bool"] or self.reference:
result += "&"
result += " " + self.name.cppValue()
result += " " + self.name.cppValue
return result
def cppLocalVariableDeclaration(self):
......@@ -665,7 +668,7 @@ class Argument:
result += self._makeVectorType()
else:
result += self.baseType.qtName()
result += " " + self.name.cppValue()
result += " " + self.name.cppValue
return result
def kumirArgumentDeclaration(self):
......@@ -800,7 +803,7 @@ class Method:
else:
argumentLine += ")"
result += argumentLine
body += "Q_UNUSED(" + argument.name.cppValue() + ") // Remove this line on implementation;\n"
body += "Q_UNUSED(" + argument.name.cppValue + ") // Remove this line on implementation;\n"
result += "\n"
if retval:
body += "return " + retval + ";\n"
......@@ -1440,7 +1443,7 @@ private:
{
return QByteArray("%s");
}
""" % (self.className, self._module.name.asciiValue())
""" % (self.className, self._module.name.asciiValue().replace("\\", "\\\\"))
def localizedModuleNameCppImplementation(self):
"""
......@@ -1456,7 +1459,7 @@ private:
// TODO non-Russian languages not implemented yet
return QString::fromUtf8("%s");
}
""" % (self.className, self._module.name.kumirValue())
""" % (self.className, self._module.name.kumirValue().replace("\\", "\\\\"))
@property
def functionListCppImplementation(self):
......@@ -1470,14 +1473,14 @@ private:
body += "result.last().accessType = TeacherModeFunction;\n"
else:
body += "result.last().accessType = PublicFunction;\n"
body += 'result.last().asciiName = QByteArray("%s");\n' % method.name.asciiValue()
body += 'result.last().asciiName = QByteArray("%s");\n' % method.name.asciiValue().replace("\\", "\\\\")
assert isinstance(method.name.data, dict)
for key, value in method.name.data.items():
qlocale = None
if key == "ru_RU":
qlocale = "QLocale::Russian"
if qlocale:
body += 'result.last().localizedNames[%s] = QString::fromUtf8("%s");\n' % (qlocale, value)
body += 'result.last().localizedNames[%s] = QString::fromUtf8("%s");\n' % (qlocale, value.replace("\\", "\\\\"))
if method.returnType:
assert isinstance(method.returnType, BaseType)
if method.returnType._name.asciiValue() == "int":
......@@ -1785,7 +1788,7 @@ private:
else:
switchBody += "decode(args[%d])" % index
switchBody += ";\n"
args += [argument.name.cppValue()]
args += [argument.name.cppValue]
if method.returnType:
returnType = method.returnType
assert isinstance(returnType, BaseType)
......@@ -1809,9 +1812,9 @@ private:
returnsAnyArgument = True
plainType = argument.baseType.qtName() in ["int", "qreal", "bool", "QString", "QChar"]
if plainType or argument.dimension > 0:
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue()
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue
else:
switchBody += "encode(%s);\n" % argument.name.cppValue()
switchBody += "encode(%s);\n" % argument.name.cppValue
else:
switchBody += "QVariant::Invalid;\n"
switchBody += " if (errorText_.length() > 0) {\n"
......@@ -1874,7 +1877,7 @@ private:
else:
switchBody += "decode(args[%d])" % index
switchBody += ";\n"
args += [argument.name.cppValue()]
args += [argument.name.cppValue]
if method.returnType:
returnType = method.returnType
assert isinstance(returnType, BaseType)
......@@ -1898,9 +1901,9 @@ private:
returnsAnyArgument = True
plainType = argument.baseType.qtName() in ["int", "qreal", "bool", "QString", "QChar"]
if plainType or argument.dimension > 0:
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue()
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue
else:
switchBody += "encode(%s);\n" % argument.name.cppValue()
switchBody += "encode(%s);\n" % argument.name.cppValue
else:
switchBody += "QVariant::Invalid;\n"
switchBody += " break;\n"
......@@ -2280,7 +2283,7 @@ class AsyncThreadCppClass(CppClassBase):
args = []
for index, argument in enumerate(method.arguments):
assert isinstance(argument, Argument)
args += [argument.name.cppValue()]
args += [argument.name.cppValue]
switchBody += " %s = " % argument.cppLocalVariableDeclaration()
if argument.dimension > 0:
switchBody += "toVector%d<%s>(args[%d])" % (
......@@ -2314,9 +2317,9 @@ class AsyncThreadCppClass(CppClassBase):
if argument.reference and not argument.constant:
plainType = argument.baseType.qtName() in ["int", "qreal", "bool", "QString", "QChar"]
if plainType or argument.dimension > 0:
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue()
switchBody += "QVariant::fromValue(%s);\n" % argument.name.cppValue
else:
switchBody += "encode(%s);\n" % argument.name.cppValue()
switchBody += "encode(%s);\n" % argument.name.cppValue
else:
switchBody += "QVariant::Invalid;\n"
switchBody += " break;\n"
......
......@@ -178,6 +178,7 @@
"Here must be \"input\" or \"output\"";"";"";"";"";"";
"Hidden part must contain only algorithm";"";"";"";"";"";
"Incompatible types";"Плохой тип параметра";"";"";"";"";
"Incomplete module name";"Не полное имя исполнителя";"";"";"";"";
"Incorrect algorithm name";"Некорректное имя алгоритма";"";"";"";"";
"Indeces was specified before";"Повторное указание индексов";"";"";"";"";
"Index is not integer";"Индекс – не целое число";"";"";"";"";
......
......@@ -344,6 +344,32 @@ void AnalizerPrivate::createModuleFromActor_stage1(Shared::ActorInterface * acto
mod->header.type = AST::ModTypeExternal;
mod->header.name = actor->localizedModuleName(QLocale::Russian);
mod->header.asciiName = actor->asciiModuleName();
if (-1 != mod->header.name.indexOf("%")) {
mod->header.nameTemplate = mod->header.name;
static const QRegExp rxTemplateParameter("%[sdfb]");
int p = 0;
Q_FOREVER {
p = rxTemplateParameter.indexIn(mod->header.nameTemplate, p);
if (-1 == p) break;
p += rxTemplateParameter.matchedLength();
const QString cap = rxTemplateParameter.cap();
QVariant::Type templateType;
const QChar ch = cap[1];
switch (ch.toAscii()) {
case 'd': templateType = QVariant::Int; break;
case 'f': templateType = QVariant::Double; break;
case 'b': templateType = QVariant::Bool; break;
default: templateType = QVariant::String;
}
mod->header.templateTypes.append(templateType);
mod->header.templateParameters.append(QVariant::Invalid);
}
mod->header.name = mod->header.name.left(mod->header.name.indexOf("%")).trimmed();
}
if (-1 != mod->header.asciiName.indexOf("%")) {
mod->header.asciiName = mod->header.asciiName.left(mod->header.asciiName.indexOf("%")).trimmed();
}
mod->impl.actor = AST::ActorPtr(actor);
ast->modules << ModulePtr(mod);
const Shared::ActorInterface::TypeList typeList = actor->typeList();
......
......@@ -307,11 +307,12 @@ SyntaxAnalizer::suggestInputOutputAutoComplete(
AST::VariablePtr var;
AST::AlgorithmPtr alg;
AST::Type type;
QVariantList templateParameters;
if (findVariable(name, contextModule, contextAlgorithm, var))
{
type = var->baseType;
}
else if (findAlgorhitm(name, contextModule, contextAlgorithm, alg)) {
else if (findAlgorithm(name, contextModule, contextAlgorithm, alg, templateParameters)) {
type = alg->header.returnType;
}
if (type.kind!=AST::TypeUser && type.kind!=AST::TypeNone) {
......@@ -735,7 +736,8 @@ QList<Shared::Analizer::Suggestion> SyntaxAnalizer::suggestExpressionAutoComplet
}
algorithmName = algorithmName.simplified();
AST::AlgorithmPtr alg;
if (findAlgorhitm(algorithmName, contextModule, contextAlgorithm, alg)) {
QVariantList templateParameters;
if (findAlgorithm(algorithmName, contextModule, contextAlgorithm, alg, templateParameters)) {
int argumentIndex = comasBefore;
if (argumentIndex<alg->header.arguments.size()) {
// Suggest a corresponding argument type value
......@@ -1527,18 +1529,18 @@ void SyntaxAnalizer::parseImport(int str)
st.data[0]->error = _("No module name");
return;
}
if (st.data.size()>2) {
for (int i=2; i<st.data.size(); i++) {
st.data[i]->error = _("Garbage afrer module name");
}
return;
}
if (st.data[1]->data.isEmpty()) {
st.data[1]->error = _("No module name");
return;
}
QString name;
if (st.data[1]->type==LxConstLiteral) {
if (st.data.size()>2) {
for (int i=2; i<st.data.size(); i++) {
st.data[i]->error = _("Garbage afrer module name");
}
return;
}
name = st.data[1]->data.trimmed();
if (name.isEmpty()) {
st.data[1]->error = _("Must be Kumir program file name");
......@@ -1593,16 +1595,51 @@ void SyntaxAnalizer::parseImport(int str)
return;
}
else {
QString localError = lexer_->testName(st.data[1]->data);
bool isTemplateName = false;
foreach (const LexemPtr lexem, st.data.mid(1)) {
name += " " + lexem->data;
}
QString localError = lexer_->testName(name);
if (localError.size()>0) {
st.data[1]->error = localError;
foreach (LexemPtr lexem, st.data.mid(1)) {
lexem->error = localError;
}
return;
}
name = st.data[1]->data.simplified();
name = name.simplified();
foreach (AST::ModulePtr module, ast_->modules) {
if (module->header.name == name && !module->header.name.startsWith("_")) {
moduleToImport = module;
break;
if (module->header.nameTemplate.length() == 0) {
// Regular module name
if (module->header.name == name && !module->header.name.startsWith("_")) {
moduleToImport = module;
break;
}
}
else {
QString namePattern = module->header.nameTemplate;
namePattern.replace("%d", "(\\d+)");
namePattern.replace("%s", "(\\w+)");
QRegExp moduleRx(namePattern);
if (-1 != moduleRx.indexIn(name)) {
const QStringList caps = moduleRx.capturedTexts().mid(1);
if (caps.size() == module->header.templateTypes.size()) {
for (int k=0; k<caps.size(); k++) {
QVariant param = QVariant(caps[k]);
param.convert(module->header.templateTypes[k]);
module->header.templateParameters[k] = param;
}
moduleToImport = module;
break;
}
}
}
}
if (!isTemplateName) {
if (st.data.size()>2) {
for (int i=2; i<st.data.size(); i++) {
st.data[i]->error = _("Garbage afrer module name");
}
return;
}
}
}
......@@ -1625,8 +1662,27 @@ void SyntaxAnalizer::parseImport(int str)
}
}
else {
st.data[1]->error = _("No such module");
return;
// Check if valid but uncomplete name
bool incompleteName = false;
foreach (AST::ModulePtr module, ast_->modules) {
if (module->header.nameTemplate.length() > 0) {
if (name == module->header.name) {
incompleteName = true;
}
}
}
if (incompleteName) {
for (int i=1; i<st.data.size(); i++) {
st.data[i]->error = _("Incomplete module name");
}
return;
}
else {
for (int i=1; i<st.data.size(); i++) {
st.data[i]->error = _("No such module");
}
return;
}
}
const QStringList conflictNames = checkForConflictingNames(moduleToImport, st.mod);
if (conflictNames.size() > 0) {
......@@ -2348,6 +2404,7 @@ void SyntaxAnalizer::parseLoopBegin(int str)
}
QString name;
AST::AlgorithmPtr dummyAlg;
QVariantList templateParameters;
for (int i=0; i<forr.size(); i++) {
if (i>0) name += " "; name += forr[i]->data;
}
......@@ -2357,7 +2414,7 @@ void SyntaxAnalizer::parseLoopBegin(int str)
else if (findGlobalVariable(name, st.mod, st.statement->loop.forVariable)) {
}
else if (findAlgorhitm(name, st.mod, st.alg, dummyAlg)) {
else if (findAlgorithm(name, st.mod, st.alg, dummyAlg, templateParameters)) {
foreach (LexemPtr l, forr) l->error = _("Algorithm can't be a loop variable");
return;
}
......@@ -2864,7 +2921,8 @@ void SyntaxAnalizer::parseAlgHeader(int str, bool onlyName, bool internalBuildFl
// Проверяем на повторное описание алгоритма
AST::AlgorithmPtr aa;
if (!isOperator && findAlgorhitm(name,st.mod, AST::AlgorithmPtr(), aa) && aa!=alg)
QVariantList aaTemplateParameters;
if (!isOperator && findAlgorithm(name,st.mod, AST::AlgorithmPtr(), aa, aaTemplateParameters) && aa!=alg)
{
for (int i=1; i<st.data.size(); i++) {
if (st.data[i]->type==LxNameAlg)
......@@ -3384,7 +3442,8 @@ QList<AST::VariablePtr> SyntaxAnalizer::parseVariables(int statementIndex, Varia
}
AST::AlgorithmPtr aa;
if (findAlgorhitm(cName, mod, alg, aa)) {
QVariantList aaTemplateParameters;
if (findAlgorithm(cName, mod, alg, aa, aaTemplateParameters)) {
group.lexems[nameStart]->error = _("Name is used by algorithm");
return result;
}
......@@ -4160,14 +4219,15 @@ QVariant SyntaxAnalizer::createConstValue(const QString & str
return result;
}
bool SyntaxAnalizer::findAlgorhitm(
bool SyntaxAnalizer::findAlgorithm(
const QString &name,
const AST::ModulePtr currentModule,
const AST::AlgorithmPtr currentAlgorithm,
AST::AlgorithmPtr &algorhitm
AST::AlgorithmPtr &algorithm,
QVariantList & templateParameters
) const
{
algorhitm.clear();
algorithm.clear();
for (int i=0; i<ast_->modules.size(); i++) {
AST::ModulePtr module = ast_->modules[i];
bool moduleAvailable =
......@@ -4192,7 +4252,7 @@ bool SyntaxAnalizer::findAlgorhitm(
for (int j=0; j<ast_->modules[i]->impl.algorhitms.size(); j++) {
AST::AlgorithmPtr alg = ast_->modules[i]->impl.algorhitms[j];
if (alg->header.name==name) {
algorhitm = alg;
algorithm = alg;
return true;
}
}
......@@ -4202,7 +4262,7 @@ bool SyntaxAnalizer::findAlgorhitm(
for (int j=0; j<ast_->modules[i]->header.algorhitms.size(); j++) {
AST::AlgorithmPtr alg = ast_->modules[i]->header.algorhitms[j];
if (alg->header.name==name && !alg->header.broken) {
algorhitm = alg;
algorithm = alg;
return true;
}
}
......@@ -4213,6 +4273,45 @@ bool SyntaxAnalizer::findAlgorhitm(
return false;
}
bool SyntaxAnalizer::findAlgorithmInModule(const QString &name,
const ModulePtr &module,
const bool allowPrivate,
const bool allowBroken,
AlgorithmPtr &algorithm,
QVariantList &templateParameters) const
{
algorithm = AlgorithmPtr();
templateParameters = QVariantList();
const QList<AlgorithmPtr> & algList = allowPrivate
? module->impl.algorhitms : module->header.algorhitms;
Q_FOREACH(AlgorithmPtr a, algList) {
bool skip = !allowBroken && a->header.broken;
if (skip) continue;
if (module->header.nameTemplate.length() > 0) {
// construct fully-qualified name and search by regexp
QString pattern = a->header.name;
for (int t=0; t<module->header.templateParameters.size(); t++) {
pattern.replace("%" + QString::number(t+1), module->header.templateParameters[t].toString());
}
pattern = pattern.simplified();
if (name == pattern) {
templateParameters = QVariantList(module->header.templateParameters);
algorithm = a;
return true;
}
}
else {
// check plaint name match
if (name == a->header.name) {
algorithm = a;
return true;
}
}
}
return false;
}
bool SyntaxAnalizer::tryInputOperatorAlgorithm(
const QString & lexem,
AST::Type & type,
......@@ -5166,7 +5265,8 @@ AST::ExpressionPtr SyntaxAnalizer::parseFunctionCall(const QList<LexemPtr> &lex
QList<LexemPtr> comas;
AST::AlgorithmPtr function;
if (!findAlgorhitm(name, mod, alg, function)) {
QVariantList functionTemplateParameters;
if (!findAlgorithm(name, mod, alg, function, functionTemplateParameters)) {
if (openBracketIndex==-1)
openBracketIndex = lexems.size();
for (int i=0; i<openBracketIndex; i++) {
......@@ -5372,8 +5472,9 @@ void SyntaxAnalizer::updateSliceDSCall(AST::ExpressionPtr expr, AST::VariablePt
{
static AST::AlgorithmPtr strlenAlg;
static AST::ModulePtr stdlibMod;
static QVariantList functionTemplateParameters;
if (!strlenAlg)
findAlgorhitm(QString::fromUtf8("длин"), stdlibMod, AST::AlgorithmPtr(), strlenAlg);
findAlgorithm(QString::fromUtf8("длин"), stdlibMod, AST::AlgorithmPtr(), strlenAlg, functionTemplateParameters);
if (expr->kind==AST::ExprFunctionCall
&& expr->function==strlenAlg
&& expr->operands.size()==0)
......@@ -5394,9 +5495,10 @@ bool SyntaxAnalizer::checkWrongDSUsage(ExpressionPtr expression)
{
static AST::AlgorithmPtr strlenAlg;
static AST::ModulePtr stdlibMod;
static QVariantList functionTemplateParameters;
bool hasError = false;
if (!strlenAlg)
findAlgorhitm(QString::fromUtf8("длин"), stdlibMod, AST::AlgorithmPtr(), strlenAlg);
findAlgorithm(QString::fromUtf8("длин"), stdlibMod, AST::AlgorithmPtr(), strlenAlg, functionTemplateParameters);
if (expression->kind==AST::ExprFunctionCall
&& expression->function==strlenAlg
&& expression->operands.size()==0)
......@@ -5473,7 +5575,8 @@ AST::ExpressionPtr SyntaxAnalizer::parseElementAccess(const QList<LexemPtr> &le
}
else {
AST::AlgorithmPtr a ;
if (findAlgorhitm(name, mod, alg, a)) {
QVariantList aTemplateParameters;
if (findAlgorithm(name, mod, alg, a, aTemplateParameters)) {
int a = qMax(0, openBracketIndex);
for (int i=a; i<lexems.size(); i++) {
lexems[i]->error = _("'[...]' instead of '(...)'");
......@@ -5717,8 +5820,9 @@ AST::ExpressionPtr SyntaxAnalizer::parseSimpleName(const std::list<LexemPtr> &l
result->baseType = AST::TypeInteger;
result->dimension = 0;
result->lexems = QList<LexemPtr>::fromStdList(lexems);
const AST::ModulePtr dummy;
findAlgorhitm(QString::fromUtf8("длин"), dummy, AST::AlgorithmPtr(), result->function);
const AST::ModulePtr dummy;
QVariantList dummyTemplateParameters;
findAlgorithm(QString::fromUtf8("длин"), dummy, AST::AlgorithmPtr(), result->function, dummyTemplateParameters);
return result;
}
if (lexems.size()>=1 &&
......@@ -5867,6 +5971,7 @@ AST::ExpressionPtr SyntaxAnalizer::parseSimpleName(const std::list<LexemPtr> &l
AST::AlgorithmPtr a;
AST::VariablePtr v;
QVariantList aTemplateParameters;
err = "";
if (retval) {
......@@ -5890,10 +5995,10 @@ AST::ExpressionPtr SyntaxAnalizer::parseSimpleName(const std::list<LexemPtr> &l
return result;
}
}
else {
if (findAlgorhitm(name, mod, alg, a)) {
else {
if (findAlgorithm(name, mod, alg, a, aTemplateParameters)) {
foreach (LexemPtr lx, lexems) lx->type = LxNameAlg;
if (a->header.arguments.size()>0) {
if (a->header.arguments.size() - aTemplateParameters.size() > 0) {
err = _("No arguments");
}
else {
......
......@@ -184,11 +184,21 @@ private /*methods*/:
, AST::ExpressionPtr argument
, const AST::ModulePtr currentModule
) const;
bool findAlgorhitm(const QString &name
bool findAlgorithm(const QString &name
, const AST::ModulePtr currentModule
, const AST::AlgorithmPtr currentAlgorithm
, AST::AlgorithmPtr & algorhitm
, QVariantList & templateParameters
) const;
bool findAlgorithmInModule(const QString &name
, const AST::ModulePtr & module
, const bool allowPrivate
, const bool allowBroken
, AST::AlgorithmPtr & algorithm
, QVariantList & templateParameters
) const;
bool findGlobalVariable(const QString &name, const AST::ModulePtr module, AST::VariablePtr & var) const;
bool findLocalVariable(const QString &name
, const AST::AlgorithmPtr alg
......
......@@ -58,6 +58,10 @@ struct ModuleHeader {
/** Module name, may be empty (for program itself)*/
QString name;
QString nameTemplate;
QVariantList templateParameters;
QList<QVariant::Type> templateTypes;
QByteArray asciiName;
/** Module source file name (might be used in case of name is empty) */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment