void NodeSerializer::visitValueBinding(const ValueBindingPtr &node) { node->getName()->accept(this); if(node->getDeclaredType()) { append(L" : "); node->getDeclaredType()->accept(this); } if(node->getInitializer()) { append(L" = "); node->getInitializer()->accept(this); } }
TEST(TestDeclaration, testLet) { PARSE_STATEMENT(L"let a : Int[] = [1, 2, 3]"); ValueBindingsPtr c; IdentifierPtr id; ValueBindingPtr a; ArrayLiteralPtr value; ArrayTypePtr type; TypeIdentifierPtr Int; ASSERT_NOT_NULL(c = std::dynamic_pointer_cast<ValueBindings>(root)); ASSERT_TRUE(c->isReadOnly()); ASSERT_EQ(1, c->numBindings()); ASSERT_NOT_NULL(a = c->get(0)); ASSERT_NOT_NULL(id = std::dynamic_pointer_cast<Identifier>(a->getName())); ASSERT_EQ(L"a", id->getIdentifier()); ASSERT_NOT_NULL(type = std::dynamic_pointer_cast<ArrayType>(a->getDeclaredType())); ASSERT_NOT_NULL(Int = std::dynamic_pointer_cast<TypeIdentifier>(type->getInnerType())); ASSERT_EQ(L"Int", Int->getName()); ASSERT_NOT_NULL(value = std::dynamic_pointer_cast<ArrayLiteral>(c->get(0)->getInitializer())); ASSERT_EQ(3, value->numElements()); ASSERT_EQ(L"1", std::dynamic_pointer_cast<IntegerLiteral>(value->getElement(0))->valueAsString); ASSERT_EQ(L"2", std::dynamic_pointer_cast<IntegerLiteral>(value->getElement(1))->valueAsString); ASSERT_EQ(L"3", std::dynamic_pointer_cast<IntegerLiteral>(value->getElement(2))->valueAsString); }
/*! * Verify type of tuple declaration */ void SemanticAnalyzer::validateTupleTypeDeclaration(const ValueBindingPtr& node) { TypePtr tupleType = nullptr; TypePtr initializerType = nullptr; if(node->getDeclaredType()) tupleType = lookupType(node->getDeclaredType()); if(node->getInitializer()) { SCOPED_SET(ctx->contextualType, tupleType); node->getInitializer()->accept(this); initializerType = node->getInitializer()->getType(); assert(initializerType != nullptr); } //now validate with name PatternPtr name = node->getName(); validateTupleTypeDeclaration(name, tupleType, initializerType); }
TEST(TestDeclaration, testVar_Typed) { PARSE_STATEMENT(L"var welcomeMessage: String"); ValueBindingsPtr vars; ValueBindingPtr var; IdentifierPtr id; TypeIdentifierPtr t; ASSERT_NOT_NULL(vars = std::dynamic_pointer_cast<ValueBindings>(root)); ASSERT_TRUE(!vars->isReadOnly()); ASSERT_EQ(1, vars->numBindings()); ASSERT_NOT_NULL(var = std::dynamic_pointer_cast<ValueBinding>(vars->get(0))); ASSERT_NOT_NULL(id = std::dynamic_pointer_cast<Identifier>(var->getName())); ASSERT_EQ(L"welcomeMessage", id->getIdentifier()); ASSERT_NOT_NULL(t = std::dynamic_pointer_cast<TypeIdentifier>(var->getDeclaredType())); ASSERT_EQ(L"String", t->getName()); }
TEST(TestDeclaration, testLet_Multiple) { PARSE_STATEMENT(L"let a=[k1 : 1, k2 : 2], b : Int = 2"); ValueBindingsPtr c; ValueBindingPtr b; IdentifierPtr id; TypeIdentifierPtr Int; DictionaryLiteralPtr dict; ASSERT_NOT_NULL(c = std::dynamic_pointer_cast<ValueBindings>(root)); ASSERT_TRUE(c->isReadOnly()); ASSERT_EQ(2, c->numBindings()); ASSERT_NOT_NULL(id = std::dynamic_pointer_cast<Identifier>(c->get(0)->getName())); ASSERT_EQ(L"a", id->getIdentifier()); ASSERT_NOT_NULL(dict = std::dynamic_pointer_cast<DictionaryLiteral>(c->get(0)->getInitializer())); ASSERT_EQ(2, dict->numElements()); ASSERT_NOT_NULL(b = c->get(1)); ASSERT_NOT_NULL(id = std::dynamic_pointer_cast<Identifier>(b->getName())); ASSERT_EQ(L"b", id->getIdentifier()); ASSERT_NOT_NULL(Int = std::dynamic_pointer_cast<TypeIdentifier>(b->getDeclaredType())); ASSERT_EQ(L"Int", Int->getName()); }
/* GRAMMAR OF A VARIABLE DECLARATION variable-declaration → variable-declaration-head pattern-initializer-list variable-declaration → variable-declaration-head variable-name type-annotation code-block variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block variable-declaration → variable-declaration-head variable-name type-annotation initializer opt willSet-didSet-block variable-declaration-head → attributes opt declaration-specifiers opt var variable-name → identifier */ DeclarationPtr Parser::parseVar(const std::vector<AttributePtr>& attrs, int specifiers) { Token token; expect(Keyword::Var, token); Flags flags(this, UNDER_VAR); //try read it as pattern-initializer-list ValueBindingsPtr ret = nodeFactory->createValueBindings(token.state); ret->setReadOnly(false); ret->setAttributes(attrs); ret->setSpecifiers(specifiers); ValueBindingPtr var = parseVariableDeclaration(); var->setSpecifiers(specifiers); ret->add(var); if(predicate(L",")) { while(match(L",")) { var = parseVariableDeclaration(); var->setSpecifiers(specifiers); ret->add(var); } return ret; } if(!match(L"{")) return ret; peek(token); IdentifierPtr name = std::dynamic_pointer_cast<Identifier>(var->getName()); tassert(token, name != nullptr, Errors::E_GETTER_SETTER_CAN_ONLY_BE_DEFINED_FOR_A_SINGLE_VARIABLE, L""); ComputedPropertyPtr prop = nodeFactory->createComputedProperty(*var->getSourceInfo()); prop->setAttributes(attrs); prop->setSpecifiers(specifiers); prop->setTypeAttributes(var->getTypeAttributes()); prop->setName(name->getIdentifier()); prop->setDeclaredType(var->getDeclaredType()); prop->setInitializer(var->getInitializer()); var = nullptr; ret = nullptr; if(token.type != TokenType::CloseBrace) { Flags flags(this, SUPPRESS_TRAILING_CLOSURE); switch(token.getKeyword()) { case Keyword::Get: case Keyword::Set: if(this->flags & UNDER_PROTOCOL) { // variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block //no code block for getter/setter for protocol std::pair<CodeBlockPtr, CodeBlockPtr> r = parseGetterSetterKeywordBlock(); prop->setGetter(r.first); prop->setSetter(r.second); } else { // variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block std::pair<CodeBlockPtr, std::pair<std::wstring, CodeBlockPtr> > r = parseGetterSetterBlock(); prop->setGetter(r.first); prop->setSetterName(r.second.first); prop->setSetter(r.second.second); } break; case Keyword::WillSet: case Keyword::DidSet: // variable-declaration → variable-declaration-head variable-name type-annotation initializer opt willSet-didSet-block parseWillSetDidSetBlock(prop); break; default: // variable-declaration → variable-declaration-head variable-name type-annotation code-block CodeBlockPtr getter = nodeFactory->createCodeBlock(token.state); prop->setGetter(getter); do { StatementPtr st = parseStatement(); getter->addStatement(st); }while(!predicate(L"}")); break; } } expect(L"}"); return prop; }
void SemanticAnalyzer::visitValueBinding(const ValueBindingPtr& node) { PatternPtr name = node->getName(); //tuple was already exploded in declaration analyzer if(name->getNodeType() == NodeType::Tuple) { validateTupleTypeDeclaration(node); } if (name->getNodeType() != NodeType::Identifier) return; if(node->getOwner()->isReadOnly() && !node->getInitializer() && ctx->currentType == nullptr) { error(node, Errors::E_LET_REQUIRES_INITIALIZER); return; } //handle type inference for temporary variable if(node->isTemporary() && node->getInitializer()) { //temporary variable always has an initializer TypePtr initializerType; TypePtr declaredType = node->getType(); SCOPED_SET(ctx->contextualType, declaredType); node->getInitializer()->accept(this); initializerType = node->getInitializer()->getType(); assert(initializerType != nullptr); if(declaredType) { //it has both type definition and initializer, then we need to check if the initializer expression matches the type annotation if(!initializerType->canAssignTo(declaredType)) { error(node, Errors::E_CANNOT_CONVERT_EXPRESSION_TYPE_2, initializerType->toString(), declaredType->toString()); return; } } else { node->setType(initializerType); } } //add implicitly constructor for Optional IdentifierPtr id = static_pointer_cast<Identifier>(node->getName()); SymbolScope* currentScope = symbolRegistry->getCurrentScope(); TypePtr declaredType = node->getType() ? node->getType() : lookupType(node->getDeclaredType()); SCOPED_SET(ctx->contextualType, declaredType); if(!declaredType && !node->getInitializer()) { error(node, Errors::E_TYPE_ANNOTATION_MISSING_IN_PATTERN); return; } SymbolPtr sym = currentScope->lookup(id->getIdentifier()); assert(sym != nullptr); SymbolPlaceHolderPtr placeholder = std::dynamic_pointer_cast<SymbolPlaceHolder>(sym); assert(placeholder != nullptr); if(declaredType) { placeholder->setType(declaredType); } ExpressionPtr initializer = node->getInitializer(); if(initializer) { placeholder->setFlags(SymbolFlagInitializing, true); ExpressionPtr initializer = transformExpression(declaredType, node->getInitializer()); node->setInitializer(initializer); TypePtr actualType = initializer->getType(); assert(actualType != nullptr); if(declaredType) { if(!Type::equals(actualType, declaredType) && !canConvertTo(initializer, declaredType)) { error(initializer, Errors::E_CANNOT_CONVERT_EXPRESSION_TYPE_2, actualType->toString(), declaredType->toString()); return; } } if(!declaredType) placeholder->setType(actualType); } assert(placeholder->getType() != nullptr); placeholder->setFlags(SymbolFlagInitializing, false); //optional type always considered initialized, compiler will make it has a default value nil TypePtr symbolType = sym->getType(); GlobalScope* global = symbolRegistry->getGlobalScope(); if(initializer || global->isOptional(symbolType) || global->isImplicitlyUnwrappedOptional(symbolType)) markInitialized(placeholder); if (initializer) placeholder->setFlags(SymbolFlagHasInitializer, true); if(node->isTemporary()) { placeholder->setFlags(SymbolFlagTemporary, true); markInitialized(placeholder); } if(!node->isTemporary()) { //check access control level DeclarationType decl = ctx->currentType ? DeclarationTypeProperty : DeclarationTypeVariable; declarationAnalyzer->verifyAccessLevel(node->getOwner(), placeholder->getType(), decl, ComponentTypeType); } if(ctx->currentType && ctx->currentType->getCategory() == Type::Protocol) { error(node, Errors::E_PROTOCOL_VAR_MUST_BE_COMPUTED_PROPERTY_1); } }