diff --git a/doc/todo.txt b/doc/todo.txt index 539fe756a..f9d3edbc8 100644 --- a/doc/todo.txt +++ b/doc/todo.txt @@ -18,8 +18,6 @@ In development: - #617 - #637 : all bt tests -------------------------------------- - - part of code can be turned into static ones - - serialize / deserialize tree : syntax tree - serialize / deserialize tree : build tree diff --git a/elenasrc3/elc/compiler.cpp b/elenasrc3/elc/compiler.cpp index c70777c1c..d800bf57f 100644 --- a/elenasrc3/elc/compiler.cpp +++ b/elenasrc3/elc/compiler.cpp @@ -1973,6 +1973,84 @@ void Compiler :: declareDictionary(Scope& scope, SyntaxNode node, Visibility vis scope.moduleScope->module->mapSection(reference | mask, false); } +void Compiler :: declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, + bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased) +{ + SyntaxNode current = node.firstChild(); + while (current != SyntaxKey::None) { + switch (current.key) { + case SyntaxKey::MetaExpression: + { + MetaScope metaScope(&scope, Scope::ScopeLevel::Class); + + evalStatement(metaScope, current); + break; + } + case SyntaxKey::MetaDictionary: + declareDictionary(scope, current, Visibility::Public, Scope::ScopeLevel::Class); + break; + case SyntaxKey::Method: + { + MethodScope methodScope(&scope); + methodScope.isExtension = scope.extensionClassRef != 0; + declareMethodAttributes(methodScope, current, methodScope.isExtension, templateBased); + + if (!current.arg.reference) { + // NOTE : an extension method must be strong-resolved + declareVMTMessage(methodScope, current, + methodScope.checkHint(MethodHint::Extension), true); + + current.setArgumentReference(methodScope.message); + } + else methodScope.message = current.arg.reference; + + if (methodScope.isYieldable()) { + if (yieldMethodNotAllowed) { + scope.raiseError(errIllegalMethod, current); + } + else yieldMethodNotAllowed = true; + } + + declareMethodMetaInfo(methodScope, current); + declareMethod(methodScope, current, scope.abstractMode, staticNotAllowed); + + if (methodScope.checkHint(MethodHint::Constructor)) { + withConstructors = true; + if ((methodScope.message & ~STATIC_MESSAGE) == scope.moduleScope->buildins.constructor_message) { + withDefaultConstructor = true; + } + else if (getArgCount(methodScope.message) == 0 && (methodScope.checkHint(MethodHint::Protected) + || methodScope.checkHint(MethodHint::Internal))) + { + // check if it is protected / iternal default constructor + ref_t dummy = 0; + ustr_t actionName = scope.module->resolveAction(getAction(methodScope.message), dummy); + if (actionName.endsWith(CONSTRUCTOR_MESSAGE2) || actionName.endsWith(CONSTRUCTOR_MESSAGE)) + withDefaultConstructor = true; + } + } + else if (methodScope.checkHint(MethodHint::Predefined)) { + auto info = scope.info.methods.get(methodScope.message); + if (!info.hints) { + // HOTFIX : the predefined method info should be saved separately + scope.info.methods.add(methodScope.message, methodScope.info); + } + else scope.raiseError(errIllegalMethod, current); + } + + if (!_logic->validateMessage(*scope.moduleScope, methodScope.info.hints, methodScope.message)) { + scope.raiseError(errIllegalMethod, current); + } + break; + } + default: + break; + } + + current = current.nextNode(); + } +} + void Compiler :: loadMetaData(ModuleScopeBase* moduleScope, ForwardResolverBase* forwardResolver, ustr_t name) { IdentifierString metaForward(META_PREFIX, name); @@ -2638,6 +2716,59 @@ void Compiler :: generateMethodDeclarations(ClassScope& scope, SyntaxNode node, verifyMultimethods(scope, node, methodKey, scope.info, implicitMultimethods); } +void Compiler :: generateClassDeclaration(ClassScope& scope, SyntaxNode node, ref_t declaredFlags) +{ + bool closed = test(scope.info.header.flags, elClosed); + + if (scope.isClassClass()) { + // generate static fields + generateClassFields(scope, node, false); + } + else { + // HOTFIX : flags / fields should be compiled only for the class itself + generateClassFlags(scope, declaredFlags); + + // generate fields + generateClassFields(scope, node, SyntaxTree::countChild(node, SyntaxKey::Field) == 1); + } + + injectVirtualCode(node, scope, closed); + + if (scope.isClassClass()) { + generateMethodDeclarations(scope, node, SyntaxKey::StaticMethod, false); + generateMethodDeclarations(scope, node, SyntaxKey::Constructor, false); + } + else { + generateMethodDeclarations(scope, node, SyntaxKey::Method, closed); + } + + bool emptyStructure = false; + bool customDispatcher = false; + bool withAbstractMethods = false; + mssg_t mixedUpVariadicMessage = 0; + _logic->validateClassDeclaration(*scope.moduleScope, _errorProcessor, scope.info, + emptyStructure, customDispatcher, withAbstractMethods, mixedUpVariadicMessage); + if (withAbstractMethods) + scope.raiseError(errAbstractMethods, node); + if (emptyStructure) + scope.raiseError(errEmptyStructure, node.findChild(SyntaxKey::Name)); + if (customDispatcher) + scope.raiseError(errDispatcherInInterface, node.findChild(SyntaxKey::Name)); + if (mixedUpVariadicMessage) { + IdentifierString messageName; + ByteCodeUtil::resolveMessageName(messageName, scope.module, mixedUpVariadicMessage); + + _errorProcessor->info(infoMixedUpVariadic, *messageName); + + scope.raiseError(errMixedUpVariadicMessage, node.findChild(SyntaxKey::Name)); + } + + _logic->tweakClassFlags(*scope.moduleScope, scope.reference, scope.info, scope.isClassClass()); + + // generate operation list if required + _logic->injectOverloadList(this, *scope.moduleScope, scope.info, scope.reference); +} + void Compiler :: generateClassFlags(ClassScope& scope, ref_t declaredFlags) { scope.info.header.flags |= declaredFlags; @@ -2870,59 +3001,6 @@ void Compiler :: generateClassFields(ClassScope& scope, SyntaxNode node, bool si } } -void Compiler :: generateClassDeclaration(ClassScope& scope, SyntaxNode node, ref_t declaredFlags) -{ - bool closed = test(scope.info.header.flags, elClosed); - - if (scope.isClassClass()) { - // generate static fields - generateClassFields(scope, node, false); - } - else { - // HOTFIX : flags / fields should be compiled only for the class itself - generateClassFlags(scope, declaredFlags); - - // generate fields - generateClassFields(scope, node, SyntaxTree:: countChild(node, SyntaxKey::Field) == 1); - } - - injectVirtualCode(node, scope, closed); - - if (scope.isClassClass()) { - generateMethodDeclarations(scope, node, SyntaxKey::StaticMethod, false); - generateMethodDeclarations(scope, node, SyntaxKey::Constructor, false); - } - else { - generateMethodDeclarations(scope, node, SyntaxKey::Method, closed); - } - - bool emptyStructure = false; - bool customDispatcher = false; - bool withAbstractMethods = false; - mssg_t mixedUpVariadicMessage = 0; - _logic->validateClassDeclaration(*scope.moduleScope, _errorProcessor, scope.info, - emptyStructure, customDispatcher, withAbstractMethods, mixedUpVariadicMessage); - if (withAbstractMethods) - scope.raiseError(errAbstractMethods, node); - if (emptyStructure) - scope.raiseError(errEmptyStructure, node.findChild(SyntaxKey::Name)); - if (customDispatcher) - scope.raiseError(errDispatcherInInterface, node.findChild(SyntaxKey::Name)); - if (mixedUpVariadicMessage) { - IdentifierString messageName; - ByteCodeUtil::resolveMessageName(messageName, scope.module, mixedUpVariadicMessage); - - _errorProcessor->info(infoMixedUpVariadic, *messageName); - - scope.raiseError(errMixedUpVariadicMessage, node.findChild(SyntaxKey::Name)); - } - - _logic->tweakClassFlags(*scope.moduleScope, scope.reference, scope.info, scope.isClassClass()); - - // generate operation list if required - _logic->injectOverloadList(this, *scope.moduleScope, scope.info, scope.reference); -} - void Compiler :: declareSymbol(SymbolScope& scope, SyntaxNode node) { declareSymbolAttributes(scope, node, false); @@ -2953,74 +3031,6 @@ void Compiler :: declareClassParent(ref_t parentRef, ClassScope& scope, SyntaxNo } -void Compiler :: resolveClassPostfixes(ClassScope& scope, SyntaxNode node, bool extensionMode) -{ - ref_t parentRef = 0; - - // analizing class postfixes : if it is a parrent, template or inline template - SyntaxNode parentNode = {}; - SyntaxNode current = node.firstChild(); - while (current != SyntaxKey::None) { - switch (current.key) { - case SyntaxKey::InlineTemplate: - if (!importInlineTemplate(scope, current, INLINE_PREFIX, node)) - scope.raiseError(errInvalidOperation, current); - break; - case SyntaxKey::Parent: - { - SyntaxNode child = current.firstChild(); - if (child == SyntaxKey::TemplateType) { - if (importTemplate(scope, child, node, true)) { - // try to import as weak template - } - else if (!parentRef) { - parentNode = current; - - parentRef = resolveStrongTypeAttribute(scope, child, extensionMode, false); - } - else if (!importTemplate(scope, child, node, false)) - scope.raiseError(errUnknownTemplate, current); - } - else if (!parentRef) { - parentNode = current; - - parentRef = resolveStrongTypeAttribute(scope, child, extensionMode, false); - } - else scope.raiseError(errInvalidSyntax, current); - - break; - } - default: - break; - } - //else if (!parentRef) { - // parentNode = baseNode; - - // parentRef = resolveStrongTypeAttribute(scope, baseNode.firstChild(), extensionMode); - //} - - current = current.nextNode(); - } - - if (scope.info.header.parentRef == scope.reference) { - // if it is a super class - if (parentRef != 0) { - scope.raiseError(errInvalidSyntax, parentNode); - } - } - else if (parentRef == 0) { - parentRef = scope.info.header.parentRef; - } - - if (extensionMode) { - //COMPLIER MAGIC : treat the parent declaration in the special way for the extension - scope.extensionClassRef = parentRef; - - declareClassParent(scope.moduleScope->buildins.superReference, scope, parentNode); - } - else declareClassParent(parentRef, scope, parentNode); -} - void Compiler :: importCode(Scope& scope, SyntaxNode node, SyntaxNode& importNode) { Interpreter interpreter(scope.moduleScope, _logic); @@ -3559,89 +3569,11 @@ void Compiler :: declareMethod(MethodScope& methodScope, SyntaxNode node, bool a } } -void Compiler :: declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, - bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased) +void Compiler :: inheritStaticMethods(ClassScope& scope, SyntaxNode classNode) { - SyntaxNode current = node.firstChild(); - while (current != SyntaxKey::None) { - switch (current.key) { - case SyntaxKey::MetaExpression: - { - MetaScope metaScope(&scope, Scope::ScopeLevel::Class); - - evalStatement(metaScope, current); - break; - } - case SyntaxKey::MetaDictionary: - declareDictionary(scope, current, Visibility::Public, Scope::ScopeLevel::Class); - break; - case SyntaxKey::Method: - { - MethodScope methodScope(&scope); - methodScope.isExtension = scope.extensionClassRef != 0; - declareMethodAttributes(methodScope, current, methodScope.isExtension, templateBased); - - if (!current.arg.reference) { - // NOTE : an extension method must be strong-resolved - declareVMTMessage(methodScope, current, - methodScope.checkHint(MethodHint::Extension), true); - - current.setArgumentReference(methodScope.message); - } - else methodScope.message = current.arg.reference; - - if (methodScope.isYieldable()) { - if (yieldMethodNotAllowed) { - scope.raiseError(errIllegalMethod, current); - } - else yieldMethodNotAllowed = true; - } - - declareMethodMetaInfo(methodScope, current); - declareMethod(methodScope, current, scope.abstractMode, staticNotAllowed); - - if (methodScope.checkHint(MethodHint::Constructor)) { - withConstructors = true; - if ((methodScope.message & ~STATIC_MESSAGE) == scope.moduleScope->buildins.constructor_message) { - withDefaultConstructor = true; - } - else if (getArgCount(methodScope.message) == 0 && (methodScope.checkHint(MethodHint::Protected) - || methodScope.checkHint(MethodHint::Internal))) - { - // check if it is protected / iternal default constructor - ref_t dummy = 0; - ustr_t actionName = scope.module->resolveAction(getAction(methodScope.message), dummy); - if (actionName.endsWith(CONSTRUCTOR_MESSAGE2) || actionName.endsWith(CONSTRUCTOR_MESSAGE)) - withDefaultConstructor = true; - } - } - else if (methodScope.checkHint(MethodHint::Predefined)) { - auto info = scope.info.methods.get(methodScope.message); - if (!info.hints) { - // HOTFIX : the predefined method info should be saved separately - scope.info.methods.add(methodScope.message, methodScope.info); - } - else scope.raiseError(errIllegalMethod, current); - } - - if (!_logic->validateMessage(*scope.moduleScope, methodScope.info.hints, methodScope.message)) { - scope.raiseError(errIllegalMethod, current); - } - break; - } - default: - break; - } - - current = current.nextNode(); - } -} - -void Compiler :: inheritStaticMethods(ClassScope& scope, SyntaxNode classNode) -{ - // inject the inherited sealed static methods - for (auto it = scope.info.attributes.start(); !it.eof(); ++it) { - auto key = it.key(); + // inject the inherited sealed static methods + for (auto it = scope.info.attributes.start(); !it.eof(); ++it) { + auto key = it.key(); if (key.value2 == ClassAttribute::SealedStatic) { auto methodInfo = scope.info.methods.get(key.value1); @@ -3663,36 +3595,6 @@ void Compiler :: inheritStaticMethods(ClassScope& scope, SyntaxNode classNode) } } -void Compiler :: declareClassClass(ClassScope& classClassScope, SyntaxNode node, ref_t parentRef) -{ - classClassScope.info.header.flags |= elClassClass; // !! IMPORTANT : classclass flags should be set - - declareClassParent(classClassScope.moduleScope->buildins.superReference, classClassScope, node); - - // inherit sealed static methods - auto parentClass = mapClassSymbol(classClassScope, parentRef); - ClassInfo parentInfo; - if (_logic->defineClassInfo(*classClassScope.moduleScope, parentInfo, parentClass.typeInfo.typeRef)) { - for (auto it = parentInfo.attributes.start(); !it.eof(); ++it) { - auto key = it.key(); - - if (key.value2 == ClassAttribute::SealedStatic) { - classClassScope.info.attributes.add(key, *it); - - auto methodInfo = parentInfo.methods.get(key.value1); - methodInfo.inherited = true; - classClassScope.info.methods.add(key.value1, methodInfo); - } - } - } - - generateClassDeclaration(classClassScope, node, 0); - inheritStaticMethods(classClassScope, node); - - // save declaration - classClassScope.save(); -} - bool inline isExtensionDeclaration(SyntaxNode node) { if (SyntaxTree::ifChildExists(node, SyntaxKey::Attribute, V_EXTENSION)) @@ -3717,78 +3619,6 @@ void Compiler :: declareFieldMetaInfos(ClassScope& scope, SyntaxNode node) } } -void Compiler :: declareClass(ClassScope& scope, SyntaxNode node) -{ - bool extensionDeclaration = isExtensionDeclaration(node); - resolveClassPostfixes(scope, node, extensionDeclaration/*, lxParent*/ ); - - ref_t declaredFlags = 0; - declareClassAttributes(scope, node, declaredFlags); - - // NOTE : due to implementation the field meta information should be analyzed before - // declaring VMT - declareFieldMetaInfos(scope, node); - - bool withConstructors = false; - bool withDefConstructor = false; - bool yieldMethodNotAllowed = test(scope.info.header.flags, elWithYieldable) || test(declaredFlags, elStructureRole); - declareVMT(scope, node, withConstructors, withDefConstructor, - yieldMethodNotAllowed, false, test(declaredFlags, elTemplatebased)); - - if (yieldMethodNotAllowed && !test(scope.info.header.flags, elWithYieldable) && !test(declaredFlags, elStructureRole)) { - // HOTFIX : trying to figure out if the yield method was declared inside declareVMT - declaredFlags |= elWithYieldable; - } - - // NOTE : generateClassDeclaration should be called for the proper class before a class class one - // due to dynamic array implementation (auto-generated default constructor should be removed) - generateClassDeclaration(scope, node, declaredFlags); - - if (_logic->isRole(scope.info)) { - // class is its own class class - scope.info.header.classRef = scope.reference; - } - else { - // define class class name - IdentifierString classClassName(scope.moduleScope->resolveFullName(scope.reference)); - classClassName.append(CLASSCLASS_POSTFIX); - - scope.info.header.classRef = scope.moduleScope->mapFullReference(*classClassName); - } - - // if it is a super class validate it, generate built-in attributes - if (scope.reference == scope.moduleScope->buildins.superReference) { - validateSuperClass(scope, node); - } - - if (scope.visibility == Visibility::Public) - scope.info.attributes.add({ 0, ClassAttribute::RuntimeLoadable }, INVALID_REF); - - // save declaration - scope.save(); - - // declare class class if it available - if (scope.info.header.classRef != scope.reference && scope.info.header.classRef != 0) { - ClassScope classClassScope((NamespaceScope*)scope.parent, scope.info.header.classRef, scope.visibility); - - if (!withDefConstructor &&!scope.abstractMode && !test(scope.info.header.flags, elDynamicRole)) { - // if default constructor has to be created - injectDefaultConstructor(scope, node, withConstructors, - test(scope.info.header.flags, elStructureRole)); - } - - declareClassClass(classClassScope, node, scope.info.header.parentRef); - - // HOTFIX : if the default constructor is private - a class cannot be inherited - if (withDefConstructor - && classClassScope.info.methods.exist(scope.moduleScope->buildins.constructor_message | STATIC_MESSAGE)) - { - scope.info.header.flags |= elFinal; - scope.save(); - } - } -} - void Compiler :: declareMembers(NamespaceScope& ns, SyntaxNode node) { SyntaxNode current = node.firstChild(); @@ -3811,9 +3641,9 @@ void Compiler :: declareMembers(NamespaceScope& ns, SyntaxNode node) } case SyntaxKey::Class: { - ClassScope classScope(&ns, current.arg.reference, ns.defaultVisibility); + Class classHelper(this, &ns, current.arg.reference, ns.defaultVisibility); - declareClass(classScope, current); + classHelper.declare(current); break; } case SyntaxKey::MetaExpression: @@ -4436,23 +4266,20 @@ ref_t Compiler :: resolvePrimitiveType(ModuleScopeBase& moduleScope, ustr_t ns, } } -void Compiler :: declareSymbolAttributes(SymbolScope& scope, SyntaxNode node, bool identifierDeclarationMode) + +void Compiler :: declareClassAttributes(ClassScope& scope, SyntaxNode node, ref_t& flags) { - bool constant = false; SyntaxNode current = node.firstChild(); while (current != SyntaxKey::None) { switch (current.key) { case SyntaxKey::Attribute: - if (!_logic->validateSymbolAttribute(current.arg.value, scope.visibility, constant, scope.isStatic)) { + if (!_logic->validateClassAttribute(current.arg.value, flags, scope.visibility)) { current.setArgumentValue(0); // HOTFIX : to prevent duplicate warnings scope.raiseWarning(WARNING_LEVEL_1, wrnInvalidHint, current); } break; case SyntaxKey::Type: - case SyntaxKey::ArrayType: - case SyntaxKey::TemplateType: - if (!identifierDeclarationMode) - scope.info.typeRef = resolveStrongTypeAttribute(scope, current, true, false); + scope.raiseError(errInvalidSyntax, current); break; default: break; @@ -4461,35 +4288,34 @@ void Compiler :: declareSymbolAttributes(SymbolScope& scope, SyntaxNode node, bo current = current.nextNode(); } - if (scope.visibility == Visibility::Public) { - scope.info.loadableInRuntime = true; - } - - if (constant && !identifierDeclarationMode) { - scope.info.symbolType = SymbolType::Constant; - - Interpreter interpreter(scope.moduleScope, _logic); - ObjectInfo operand = evalExpression(interpreter, scope, node.findChild(SyntaxKey::GetExpression).firstChild(), true); - if (operand.kind == ObjectKind::IntLiteral) { - NamespaceScope* nsScope = Scope::getScope(scope, Scope::ScopeLevel::Namespace); - nsScope->defineIntConstant(scope.reference, operand.extra); + // handle the abstract flag + if (test(scope.info.header.flags, elAbstract)) { + if (!test(flags, elAbstract)) { + scope.abstractBasedMode = true; + scope.info.header.flags &= ~elAbstract; } + else scope.abstractMode = true; } + else scope.abstractMode = test(flags, elAbstract); } -void Compiler :: declareClassAttributes(ClassScope& scope, SyntaxNode node, ref_t& flags) +void Compiler :: declareSymbolAttributes(SymbolScope& scope, SyntaxNode node, bool identifierDeclarationMode) { + bool constant = false; SyntaxNode current = node.firstChild(); while (current != SyntaxKey::None) { switch (current.key) { case SyntaxKey::Attribute: - if (!_logic->validateClassAttribute(current.arg.value, flags, scope.visibility)) { + if (!_logic->validateSymbolAttribute(current.arg.value, scope.visibility, constant, scope.isStatic)) { current.setArgumentValue(0); // HOTFIX : to prevent duplicate warnings scope.raiseWarning(WARNING_LEVEL_1, wrnInvalidHint, current); } break; case SyntaxKey::Type: - scope.raiseError(errInvalidSyntax, current); + case SyntaxKey::ArrayType: + case SyntaxKey::TemplateType: + if (!identifierDeclarationMode) + scope.info.typeRef = resolveStrongTypeAttribute(scope, current, true, false); break; default: break; @@ -4498,15 +4324,20 @@ void Compiler :: declareClassAttributes(ClassScope& scope, SyntaxNode node, ref_ current = current.nextNode(); } - // handle the abstract flag - if (test(scope.info.header.flags, elAbstract)) { - if (!test(flags, elAbstract)) { - scope.abstractBasedMode = true; - scope.info.header.flags &= ~elAbstract; + if (scope.visibility == Visibility::Public) { + scope.info.loadableInRuntime = true; + } + + if (constant && !identifierDeclarationMode) { + scope.info.symbolType = SymbolType::Constant; + + Interpreter interpreter(scope.moduleScope, _logic); + ObjectInfo operand = evalExpression(interpreter, scope, node.findChild(SyntaxKey::GetExpression).firstChild(), true); + if (operand.kind == ObjectKind::IntLiteral) { + NamespaceScope* nsScope = Scope::getScope(scope, Scope::ScopeLevel::Namespace); + nsScope->defineIntConstant(scope.reference, operand.extra); } - else scope.abstractMode = true; } - else scope.abstractMode = test(flags, elAbstract); } inline bool isMethodKind(ref_t hint) @@ -8788,167 +8619,30 @@ void Compiler :: compileCustomDispatcher(BuildTreeWriter& writer, ClassScope& sc scope.save(); } -void Compiler :: compileVMT(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, - bool exclusiveMode, bool ignoreAutoMultimethod) +void Compiler :: compileExpressionMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) { - SyntaxNode current = node.firstChild(); - while (current != SyntaxKey::None) { - switch (current.key) { - case SyntaxKey::Method: - { - if (exclusiveMode - && (ignoreAutoMultimethod == SyntaxTree::ifChildExists(current, SyntaxKey::Autogenerated, -1))) - { - current = current.nextNode(); - continue; - } + beginMethod(writer, scope, node, BuildKey::Method, false); - MethodScope methodScope(&scope); - initializeMethod(scope, methodScope, current); + CodeScope codeScope(&scope); -#ifdef FULL_OUTOUT_INFO - IdentifierString messageName; - ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); + // new stack frame + writer.appendNode(BuildKey::OpenFrame); - // !! temporal - if (messageName.compare("static:getItem<'IntNumber,'Object>[3]")) - methodScope.message |= 0; + // stack should contains current self reference + // the original message should be restored if it is a generic method + scope.selfLocal = codeScope.newLocal(); + writer.appendNode(BuildKey::Assigning, scope.selfLocal); - _errorProcessor->info(infoCurrentMethod, *messageName); -#endif // FULL_OUTOUT_INFO + compileRetExpression(writer, codeScope, node, EAttr::None); - // if it is a dispatch handler - if (methodScope.message == scope.moduleScope->buildins.dispatch_message) { - compileDispatcherMethod(writer, methodScope, current, - test(scope.info.header.flags, elWithGenerics), - test(scope.info.header.flags, elWithVariadics)); - } - // if it is an abstract one - else if (methodScope.checkHint(MethodHint::Abstract)) { - compileAbstractMethod(writer, methodScope, current, scope.abstractMode); - } - // if it is an initializer - else if (methodScope.checkHint(MethodHint::Initializer)) { - compileInitializerMethod(writer, methodScope, node); - } - // if it is a normal method - else compileMethod(writer, methodScope, current); - break; - } - case SyntaxKey::Constructor: - if (_logic->isRole(scope.info)) { - scope.raiseError(errIllegalConstructor, node); - } - break; - case SyntaxKey::StaticMethod: - if (_logic->isRole(scope.info)) { - scope.raiseError(errIllegalStaticMethod, node); - } - break; - case SyntaxKey::StaticInitializerMethod: - compileStaticInitializerMethod(writer, scope, current); - break; - default: - break; - } + writer.appendNode(BuildKey::CloseFrame); - current = current.nextNode(); - } + codeScope.syncStack(&scope); - // if the VMT conatains newly defined generic / variadic handlers, overrides default one - if (testany(scope.info.header.flags, elWithGenerics | elWithVariadics) - && scope.info.methods.get(scope.moduleScope->buildins.dispatch_message).inherited) - { - compileCustomDispatcher(writer, scope); - } + endMethod(writer, scope); } -void Compiler :: compileClassVMT(BuildTreeWriter& writer, ClassScope& classClassScope, ClassScope& scope, SyntaxNode node) -{ - SyntaxNode current = node.firstChild(); - // first pass - compile constructors - while (current != SyntaxKey::None) { - switch (current.key) { - case SyntaxKey::Constructor: - { - MethodScope methodScope(&scope); - initializeMethod(classClassScope, methodScope, current); - methodScope.constructorMode = true; - - #ifdef FULL_OUTOUT_INFO - IdentifierString messageName; - ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); - - _errorProcessor->info(infoCurrentMethod, *messageName); - #endif // FULL_OUTOUT_INFO - - compileConstructor(writer, methodScope, classClassScope, current, scope.isAbstract()); - break; - } - default: - break; - } - current = current.nextNode(); - } - - // second pass - compile static methods - current = node.firstChild(); - while (current != SyntaxKey::None) { - switch (current.key) { - case SyntaxKey::StaticMethod: - { - MethodScope methodScope(&classClassScope); - initializeMethod(classClassScope, methodScope, current); - -#ifdef FULL_OUTOUT_INFO - IdentifierString messageName; - ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); - - _errorProcessor->info(infoCurrentMethod, *messageName); -#endif // FULL_OUTOUT_INFO - - compileMethod(writer, methodScope, current); - - break; - } - default: - break; - } - current = current.nextNode(); - } - - // if the VMT conatains newly defined generic / variadic handlers, overrides default one - if (testany(classClassScope.info.header.flags, elWithGenerics | elWithVariadics) - && classClassScope.info.methods.get(scope.moduleScope->buildins.dispatch_message).inherited) - { - compileCustomDispatcher(writer, classClassScope); - } -} - -void Compiler :: compileExpressionMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) -{ - beginMethod(writer, scope, node, BuildKey::Method, false); - - CodeScope codeScope(&scope); - - // new stack frame - writer.appendNode(BuildKey::OpenFrame); - - // stack should contains current self reference - // the original message should be restored if it is a generic method - scope.selfLocal = codeScope.newLocal(); - writer.appendNode(BuildKey::Assigning, scope.selfLocal); - - compileRetExpression(writer, codeScope, node, EAttr::None); - - writer.appendNode(BuildKey::CloseFrame); - - codeScope.syncStack(&scope); - - endMethod(writer, scope); -} - -void Compiler :: compileClosureMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) +void Compiler :: compileClosureMethod(BuildTreeWriter& writer, MethodScope& scope, SyntaxNode node) { ClassScope* classScope = Scope::getScope(scope, Scope::ScopeLevel::Class); @@ -9064,6 +8758,143 @@ void Compiler :: compileClosureClass(BuildTreeWriter& writer, ClassScope& scope, scope.save(); } +void Compiler :: compileVMT(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, + bool exclusiveMode, bool ignoreAutoMultimethod) +{ + SyntaxNode current = node.firstChild(); + while (current != SyntaxKey::None) { + switch (current.key) { + case SyntaxKey::Method: + { + if (exclusiveMode + && (ignoreAutoMultimethod == SyntaxTree::ifChildExists(current, SyntaxKey::Autogenerated, -1))) + { + current = current.nextNode(); + continue; + } + + MethodScope methodScope(&scope); + initializeMethod(scope, methodScope, current); + +#ifdef FULL_OUTOUT_INFO + IdentifierString messageName; + ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); + + // !! temporal + if (messageName.compare("static:getItem<'IntNumber,'Object>[3]")) + methodScope.message |= 0; + + _errorProcessor->info(infoCurrentMethod, *messageName); +#endif // FULL_OUTOUT_INFO + + // if it is a dispatch handler + if (methodScope.message == scope.moduleScope->buildins.dispatch_message) { + compileDispatcherMethod(writer, methodScope, current, + test(scope.info.header.flags, elWithGenerics), + test(scope.info.header.flags, elWithVariadics)); + } + // if it is an abstract one + else if (methodScope.checkHint(MethodHint::Abstract)) { + compileAbstractMethod(writer, methodScope, current, scope.abstractMode); + } + // if it is an initializer + else if (methodScope.checkHint(MethodHint::Initializer)) { + compileInitializerMethod(writer, methodScope, node); + } + // if it is a normal method + else compileMethod(writer, methodScope, current); + break; + } + case SyntaxKey::Constructor: + if (_logic->isRole(scope.info)) { + scope.raiseError(errIllegalConstructor, node); + } + break; + case SyntaxKey::StaticMethod: + if (_logic->isRole(scope.info)) { + scope.raiseError(errIllegalStaticMethod, node); + } + break; + case SyntaxKey::StaticInitializerMethod: + compileStaticInitializerMethod(writer, scope, current); + break; + default: + break; + } + + current = current.nextNode(); + } + + // if the VMT conatains newly defined generic / variadic handlers, overrides default one + if (testany(scope.info.header.flags, elWithGenerics | elWithVariadics) + && scope.info.methods.get(scope.moduleScope->buildins.dispatch_message).inherited) + { + compileCustomDispatcher(writer, scope); + } +} + +void Compiler :: compileClassVMT(BuildTreeWriter& writer, ClassScope& classClassScope, ClassScope& scope, SyntaxNode node) +{ + SyntaxNode current = node.firstChild(); + // first pass - compile constructors + while (current != SyntaxKey::None) { + switch (current.key) { + case SyntaxKey::Constructor: + { + MethodScope methodScope(&scope); + initializeMethod(classClassScope, methodScope, current); + methodScope.constructorMode = true; + +#ifdef FULL_OUTOUT_INFO + IdentifierString messageName; + ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); + + _errorProcessor->info(infoCurrentMethod, *messageName); +#endif // FULL_OUTOUT_INFO + + compileConstructor(writer, methodScope, classClassScope, current, scope.isAbstract()); + break; + } + default: + break; + } + current = current.nextNode(); + } + + // second pass - compile static methods + current = node.firstChild(); + while (current != SyntaxKey::None) { + switch (current.key) { + case SyntaxKey::StaticMethod: + { + MethodScope methodScope(&classClassScope); + initializeMethod(classClassScope, methodScope, current); + +#ifdef FULL_OUTOUT_INFO + IdentifierString messageName; + ByteCodeUtil::resolveMessageName(messageName, scope.module, methodScope.message); + + _errorProcessor->info(infoCurrentMethod, *messageName); +#endif // FULL_OUTOUT_INFO + + compileMethod(writer, methodScope, current); + + break; + } + default: + break; + } + current = current.nextNode(); + } + + // if the VMT conatains newly defined generic / variadic handlers, overrides default one + if (testany(classClassScope.info.header.flags, elWithGenerics | elWithVariadics) + && classClassScope.info.methods.get(scope.moduleScope->buildins.dispatch_message).inherited) + { + compileCustomDispatcher(writer, classClassScope); + } +} + bool isEmbeddableDispatcher(ModuleScopeBase* moduleScope, SyntaxNode current) { SyntaxNode attr = current.firstChild(); @@ -10150,6 +9981,182 @@ void Compiler :: declareModuleExtensionDispatcher(NamespaceScope& scope, SyntaxN } } +// --- Compiler::Class --- +Compiler::Class :: Class(Compiler* compiler, Scope* parent, ref_t reference, Visibility visibility) + : compiler(compiler), scope(parent, reference, visibility) +{ +} + +void Compiler::Class :: declare(SyntaxNode node) +{ + bool extensionDeclaration = isExtensionDeclaration(node); + resolveClassPostfixes(node, extensionDeclaration/*, lxParent*/); + + ref_t declaredFlags = 0; + compiler->declareClassAttributes(scope, node, declaredFlags); + + // NOTE : due to implementation the field meta information should be analyzed before + // declaring VMT + compiler->declareFieldMetaInfos(scope, node); + + bool withConstructors = false; + bool withDefConstructor = false; + bool yieldMethodNotAllowed = test(scope.info.header.flags, elWithYieldable) || test(declaredFlags, elStructureRole); + compiler->declareVMT(scope, node, withConstructors, withDefConstructor, + yieldMethodNotAllowed, false, test(declaredFlags, elTemplatebased)); + + if (yieldMethodNotAllowed && !test(scope.info.header.flags, elWithYieldable) && !test(declaredFlags, elStructureRole)) { + // HOTFIX : trying to figure out if the yield method was declared inside declareVMT + declaredFlags |= elWithYieldable; + } + + // NOTE : generateClassDeclaration should be called for the proper class before a class class one + // due to dynamic array implementation (auto-generated default constructor should be removed) + compiler->generateClassDeclaration(scope, node, declaredFlags); + + if (compiler->_logic->isRole(scope.info)) { + // class is its own class class + scope.info.header.classRef = scope.reference; + } + else { + // define class class name + IdentifierString classClassName(scope.moduleScope->resolveFullName(scope.reference)); + classClassName.append(CLASSCLASS_POSTFIX); + + scope.info.header.classRef = scope.moduleScope->mapFullReference(*classClassName); + } + + // if it is a super class validate it, generate built-in attributes + if (scope.reference == scope.moduleScope->buildins.superReference) { + compiler->validateSuperClass(scope, node); + } + + if (scope.visibility == Visibility::Public) + scope.info.attributes.add({ 0, ClassAttribute::RuntimeLoadable }, INVALID_REF); + + // save declaration + scope.save(); + + // declare class class if it available + if (scope.info.header.classRef != scope.reference && scope.info.header.classRef != 0) { + ClassScope classClassScope((NamespaceScope*)scope.parent, scope.info.header.classRef, scope.visibility); + + if (!withDefConstructor && !scope.abstractMode && !test(scope.info.header.flags, elDynamicRole)) { + // if default constructor has to be created + compiler->injectDefaultConstructor(scope, node, withConstructors, + test(scope.info.header.flags, elStructureRole)); + } + + declareClassClass(classClassScope, node, scope.info.header.parentRef); + + // HOTFIX : if the default constructor is private - a class cannot be inherited + if (withDefConstructor + && classClassScope.info.methods.exist(scope.moduleScope->buildins.constructor_message | STATIC_MESSAGE)) + { + scope.info.header.flags |= elFinal; + scope.save(); + } + } +} + +void Compiler::Class :: resolveClassPostfixes(SyntaxNode node, bool extensionMode) +{ + ref_t parentRef = 0; + + // analizing class postfixes : if it is a parrent, template or inline template + SyntaxNode parentNode = {}; + SyntaxNode current = node.firstChild(); + while (current != SyntaxKey::None) { + switch (current.key) { + case SyntaxKey::InlineTemplate: + if (!compiler->importInlineTemplate(scope, current, INLINE_PREFIX, node)) + scope.raiseError(errInvalidOperation, current); + break; + case SyntaxKey::Parent: + { + SyntaxNode child = current.firstChild(); + if (child == SyntaxKey::TemplateType) { + if (compiler->importTemplate(scope, child, node, true)) { + // try to import as weak template + } + else if (!parentRef) { + parentNode = current; + + parentRef = compiler->resolveStrongTypeAttribute(scope, child, extensionMode, false); + } + else if (!compiler->importTemplate(scope, child, node, false)) + scope.raiseError(errUnknownTemplate, current); + } + else if (!parentRef) { + parentNode = current; + + parentRef = compiler->resolveStrongTypeAttribute(scope, child, extensionMode, false); + } + else scope.raiseError(errInvalidSyntax, current); + + break; + } + default: + break; + } + //else if (!parentRef) { + // parentNode = baseNode; + + // parentRef = resolveStrongTypeAttribute(scope, baseNode.firstChild(), extensionMode); + //} + + current = current.nextNode(); + } + + if (scope.info.header.parentRef == scope.reference) { + // if it is a super class + if (parentRef != 0) { + scope.raiseError(errInvalidSyntax, parentNode); + } + } + else if (parentRef == 0) { + parentRef = scope.info.header.parentRef; + } + + if (extensionMode) { + //COMPLIER MAGIC : treat the parent declaration in the special way for the extension + scope.extensionClassRef = parentRef; + + compiler->declareClassParent(scope.moduleScope->buildins.superReference, scope, parentNode); + } + else compiler->declareClassParent(parentRef, scope, parentNode); +} + +void Compiler::Class :: declareClassClass(ClassScope& classClassScope, SyntaxNode node, ref_t parentRef) +{ + classClassScope.info.header.flags |= elClassClass; // !! IMPORTANT : classclass flags should be set + + compiler->declareClassParent(classClassScope.moduleScope->buildins.superReference, classClassScope, node); + + // inherit sealed static methods + auto parentClass = mapClassSymbol(classClassScope, parentRef); + ClassInfo parentInfo; + if (compiler->_logic->defineClassInfo(*classClassScope.moduleScope, parentInfo, parentClass.typeInfo.typeRef)) { + for (auto it = parentInfo.attributes.start(); !it.eof(); ++it) { + auto key = it.key(); + + if (key.value2 == ClassAttribute::SealedStatic) { + classClassScope.info.attributes.add(key, *it); + + auto methodInfo = parentInfo.methods.get(key.value1); + methodInfo.inherited = true; + classClassScope.info.methods.add(key.value1, methodInfo); + } + } + } + + compiler->generateClassDeclaration(classClassScope, node, 0); + compiler->inheritStaticMethods(classClassScope, node); + + // save declaration + classClassScope.save(); +} + // --- Compiler::Expression --- Compiler::Expression :: Expression(Compiler* compiler, CodeScope& codeScope, BuildTreeWriter& writer) diff --git a/elenasrc3/elc/compiler.h b/elenasrc3/elc/compiler.h index 2cd88e45c..627ce21b0 100644 --- a/elenasrc3/elc/compiler.h +++ b/elenasrc3/elc/compiler.h @@ -1043,6 +1043,23 @@ namespace elena_lang } }; + class Class + { + friend class Compiler; + + Compiler* compiler; + ClassScope scope; + + void resolveClassPostfixes(SyntaxNode node, bool extensionMode); + + void declareClassClass(ClassScope& classClassScope, SyntaxNode node, ref_t parentRef); + + public: + void declare(SyntaxNode node); + + Class(Compiler* compiler, Scope* parent, ref_t reference, Visibility visibility); + }; + class Expression { friend class Compiler; @@ -1182,6 +1199,7 @@ namespace elena_lang Expression(Compiler* compiler, SourceScope& symbolScope, BuildTreeWriter& writer); }; + friend class Class; friend class Expression; private: @@ -1276,9 +1294,10 @@ namespace elena_lang ref_t declareMultiType(Scope& scope, SyntaxNode& node, ref_t elementRef); - void declareTemplateAttributes(TemplateScope& scope, SyntaxNode node, IdentifierString& postfix); - void declareSymbolAttributes(SymbolScope& scope, SyntaxNode node, bool identifierDeclarationMode); void declareClassAttributes(ClassScope& scope, SyntaxNode node, ref_t& fldeclaredFlagsags); + + void declareTemplateAttributes(TemplateScope& scope, SyntaxNode node, IdentifierString& postfix); + void declareSymbolAttributes(SymbolScope& scope, SyntaxNode node, bool identifierDeclarationMode); void declareFieldAttributes(ClassScope& scope, SyntaxNode node, FieldAttributes& mode); void declareMethodAttributes(MethodScope& scope, SyntaxNode node, bool exensionMode, bool templateBased); void declareArgumentAttributes(MethodScope& scope, SyntaxNode node, TypeInfo& typeInfo, bool declarationMode); @@ -1287,6 +1306,9 @@ namespace elena_lang void declareDictionary(Scope& scope, SyntaxNode node, Visibility visibility, Scope::ScopeLevel level); + void declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, + bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased); + void registerTemplateSignature(TemplateScope& scope, SyntaxNode node, IdentifierString& signature); void registerExtensionTemplateMethod(TemplateScope& scope, SyntaxNode& node); void registerExtensionTemplate(TemplateScope& scope, SyntaxNode& node); @@ -1324,14 +1346,13 @@ namespace elena_lang void generateMethodDeclarations(ClassScope& scope, SyntaxNode node, SyntaxKey methodKey, bool closed); void generateClassField(ClassScope& scope, SyntaxNode node, FieldAttributes& attrs, bool singleField); void generateClassStaticField(ClassScope& scope, SyntaxNode node, FieldAttributes& attrs); - void generateClassFields(ClassScope& scope, SyntaxNode node, bool singleField); + void generateClassFields(ClassScope& scope, SyntaxNode node, bool singleField); void generateClassDeclaration(ClassScope& scope, SyntaxNode node, ref_t declaredFlags); bool declareVariable(Scope& scope, SyntaxNode terminal, TypeInfo typeInfo, bool ignoreDuplicate); bool declareYieldVariable(Scope& scope, ustr_t name, TypeInfo typeInfo); - void declareClassParent(ref_t parentRef, ClassScope& scope, SyntaxNode node); - void resolveClassPostfixes(ClassScope& scope, SyntaxNode node, bool extensionMode); + void declareClassParent(ref_t parentRef, ClassScope& scope, SyntaxNode node); int resolveArraySize(Scope& scope, SyntaxNode node); @@ -1351,12 +1372,8 @@ namespace elena_lang void declareMetaInfo(Scope& scope, SyntaxNode node); void declareMethodMetaInfo(MethodScope& scope, SyntaxNode node); void declareMethod(MethodScope& scope, SyntaxNode node, bool abstractMode, bool staticNotAllowed); - void declareVMT(ClassScope& scope, SyntaxNode node, bool& withConstructors, bool& withDefaultConstructor, - bool& yieldMethodNotAllowed, bool staticNotAllowed, bool templateBased); - void declareSymbol(SymbolScope& scope, SyntaxNode node); - void declareClassClass(ClassScope& classClassScope, SyntaxNode node, ref_t parentRef); - void declareClass(ClassScope& scope, SyntaxNode node); + void declareSymbol(SymbolScope& scope, SyntaxNode node); void declareNamespace(NamespaceScope& ns, SyntaxNode node, bool ignoreImport = false, bool ignoreExtensions = false); @@ -1483,9 +1500,10 @@ namespace elena_lang void compileCustomDispatcher(BuildTreeWriter& writer, ClassScope& scope); void compileNestedClass(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, ref_t parentRef); void compileClosureClass(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node); + void compileVMT(BuildTreeWriter& writer, ClassScope& scope, SyntaxNode node, bool exclusiveMode = false, bool ignoreAutoMultimethod = false); - void compileClassVMT(BuildTreeWriter& writer, ClassScope& classClassScope, ClassScope& scope, SyntaxNode node); + void compileClassVMT(BuildTreeWriter& writer, ClassScope& classClassScope, ClassScope& scope, SyntaxNode node); void compileSymbol(BuildTreeWriter& writer, SymbolScope& scope, SyntaxNode node); void compileClassSymbol(BuildTreeWriter& writer, ClassScope& scope); diff --git a/elenasrc3/elena-tests/common.cpp b/elenasrc3/elena-tests/common.cpp index 1d9f38c57..a682f3afd 100644 --- a/elenasrc3/elena-tests/common.cpp +++ b/elenasrc3/elena-tests/common.cpp @@ -1 +1,20 @@ +//--------------------------------------------------------------------------- +// E L E N A P r o j e c t: ELENA Compiler +// +// This header contains ELENA Test Common implementation +// (C)2024, by Aleksey Rakov +//--------------------------------------------------------------------------- + + #include "pch.h" +// -------------------------------------------------------------------------- +#include "common.h" + +using namespace elena_lang; + +// --- CompilerEnvironment --- + +CompilerEnvironment :: CompilerEnvironment() +{ + +} \ No newline at end of file diff --git a/elenasrc3/elena-tests/common.h b/elenasrc3/elena-tests/common.h index f23cddc62..5a2d8e1e1 100644 --- a/elenasrc3/elena-tests/common.h +++ b/elenasrc3/elena-tests/common.h @@ -5,7 +5,17 @@ // (C)2024, by Aleksey Rakov //--------------------------------------------------------------------------- -#ifndef COMMON_H -#define COMMON_H +#ifndef TESTS_COMMON_H +#define TESTS_COMMON_H -#endif \ No newline at end of file +namespace elena_lang +{ + // --- CompilerEnvironment --- + class CompilerEnvironment + { + public: + CompilerEnvironment(); + }; +} + +#endif // TESTS_COMMON_H \ No newline at end of file diff --git a/elenasrc3/elena-tests/compiler_tests.cpp b/elenasrc3/elena-tests/compiler_tests.cpp index f0d75b404..27a52cb8a 100644 --- a/elenasrc3/elena-tests/compiler_tests.cpp +++ b/elenasrc3/elena-tests/compiler_tests.cpp @@ -2,9 +2,18 @@ // ------------------------------------------------ #include "bt_optimization.h" +//#include "elena.h" + +#include "common.h" + using namespace elena_lang; TEST_F(BTOptimization1, CompilerTest) { + + CompilerEnvironment env; + + //Compiler* compiler = /*env.createCompiler()*/nullptr; + EXPECT_EQ(1, 1); EXPECT_TRUE(true); } \ No newline at end of file diff --git a/elenasrc3/elena-tests/elena-tests.vcxproj b/elenasrc3/elena-tests/elena-tests.vcxproj index 0d0684b6f..733adc20a 100644 --- a/elenasrc3/elena-tests/elena-tests.vcxproj +++ b/elenasrc3/elena-tests/elena-tests.vcxproj @@ -55,13 +55,16 @@ - Use + NotUsing pch.h Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 + ..\common;..\engine;%(AdditionalIncludeDirectories) + Default + stdc17 true @@ -77,6 +80,7 @@ EnableFastChecks MultiThreadedDebugDLL Level3 + ..\common;..\engine;%(AdditionalIncludeDirectories) true @@ -91,6 +95,7 @@ MultiThreadedDLL Level3 ProgramDatabase + ..\common;..\engine;%(AdditionalIncludeDirectories) true @@ -107,6 +112,7 @@ MultiThreadedDLL Level3 ProgramDatabase + ..\common;..\engine;%(AdditionalIncludeDirectories) true @@ -117,11 +123,11 @@ - + - +