Example #1
0
// scopeInCollectionExpression scopes an 'in' collection expression in the SRG.
func (sb *scopeBuilder) scopeInCollectionExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the left and right expressions.
	leftScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionLeftExpr), context)
	rightScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionRightExpr), context)

	// Ensure that both scopes are valid.
	if !leftScope.GetIsValid() || !rightScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Ensure that the right side has a 'contains' operator defined.
	rightType := rightScope.ResolvedTypeRef(sb.sg.tdg)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	operator, rerr := rightType.ResolveAccessibleMember("contains", module, typegraph.MemberResolutionOperator)
	if rerr != nil {
		sb.decorateWithError(node, "Operator 'contains' is not defined on type '%v'", rightType)
		return newScope().Invalid().GetScope()
	}

	// Ensure the right side is not nullable.
	if rightType.NullValueAllowed() {
		sb.decorateWithError(node, "Cannot invoke operator 'in' on nullable value of type '%v'", rightType)
		return newScope().Invalid().GetScope()
	}

	// Ensure that the left side can be used as the operator's parameter.
	parameterType := operator.ParameterTypes()[0].TransformUnder(rightType)
	leftType := leftScope.ResolvedTypeRef(sb.sg.tdg)
	if serr := leftType.CheckSubTypeOf(parameterType); serr != nil {
		sb.decorateWithError(node, "Cannot invoke operator 'in' with value of type '%v': %v", leftType, serr)
		return newScope().Invalid().GetScope()
	}

	return newScope().Valid().CallsOperator(operator).Resolving(sb.sg.tdg.BoolTypeReference()).GetScope()
}
Example #2
0
// scopeAssignedValue scopes a named assigned value exported into the context.
func (sb *scopeBuilder) scopeAssignedValue(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// If the assigned value's name is _, then it is anonymous scope.
	if node.Get(parser.NodeNamedValueName) == ANONYMOUS_REFERENCE {
		return newScope().ForAnonymousScope(sb.sg.tdg).GetScope()
	}

	// If the assigned value is under a rejection, then it is always an error (but nullable, as it
	// may not be present always).
	if _, ok := node.TryGetIncomingNode(parser.NodeAssignedRejection); ok {
		return newScope().Valid().Assignable(sb.sg.tdg.ErrorTypeReference().AsNullable()).GetScope()
	}

	// Otherwise, the value is the assignment of the parent statement's expression.
	parentNode := node.GetIncomingNode(parser.NodeAssignedDestination)
	switch parentNode.Kind() {
	case parser.NodeTypeResolveStatement:
		// The assigned value exported by a resolve statement has the type of its expression.
		exprScope := sb.getScope(parentNode.GetNode(parser.NodeResolveStatementSource), context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		// If the parent node has a rejection, then the expression may be null.
		exprType := exprScope.ResolvedTypeRef(sb.sg.tdg)
		if _, ok := parentNode.TryGetNode(parser.NodeAssignedRejection); ok {
			exprType = exprType.AsNullable()
		}

		return newScope().Valid().Assignable(exprType).GetScope()

	default:
		panic(fmt.Sprintf("Unknown node exporting an assigned value: %v", parentNode.Kind()))
		return newScope().Invalid().GetScope()
	}
}
Example #3
0
// scopeUnaryExpression scopes a unary expression in the SRG.
func (sb *scopeBuilder) scopeUnaryExpression(node compilergraph.GraphNode, opName string, predicate compilergraph.Predicate, context scopeContext) *scopeInfoBuilder {
	// Get the scope of the sub expression.
	childScope := sb.getScope(node.GetNode(predicate), context)

	// Ensure that the child scope is valid.
	if !childScope.GetIsValid() {
		return newScope().Invalid()
	}

	// Ensure that the operator exists under the resolved type.
	childType := childScope.ResolvedTypeRef(sb.sg.tdg)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	operator, rerr := childType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator)
	if rerr != nil {
		sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, childType)
		return newScope().Invalid()
	}

	returnType, _ := operator.ReturnType()

	// Check for nullable values.
	if childType.NullValueAllowed() {
		sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, childType)
		return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(childType))
	}

	return newScope().Valid().CallsOperator(operator).Resolving(returnType.TransformUnder(childType))
}
Example #4
0
// buildStringLiteral builds the CodeDOM for a string literal.
func (db *domBuilder) buildStringLiteral(node compilergraph.GraphNode) codedom.Expression {
	stringValueStr := node.Get(parser.NodeStringLiteralExpressionValue)
	if stringValueStr[0] == '`' {
		unquoted := stringValueStr[1 : len(stringValueStr)-1]
		stringValueStr = strconv.Quote(unquoted)
	}

	return codedom.NominalWrapping(codedom.LiteralValue(stringValueStr, node), db.scopegraph.TypeGraph().StringType(), node)
}
Example #5
0
// buildStreamMemberAccessExpression builds the CodeDOM for a stream member access expression (*.)
func (db *domBuilder) buildStreamMemberAccessExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeMemberAccessChildExpr)
	memberName := codedom.LiteralValue("'"+node.Get(parser.NodeMemberAccessIdentifier)+"'", node)

	return codedom.RuntimeFunctionCall(codedom.StreamMemberAccessFunction,
		[]codedom.Expression{childExpr, memberName},
		node,
	)
}
Example #6
0
// buildVarStatement builds the CodeDOM for a variable statement.
func (db *domBuilder) buildVarStatement(node compilergraph.GraphNode) codedom.Statement {
	name := node.Get(parser.NodeVariableStatementName)
	initExpr, _ := db.tryGetExpression(node, parser.NodeVariableStatementExpression)
	if initExpr == nil {
		// If no init expression was specified, then the variable is initialized to null.
		initExpr = codedom.LiteralValue("null", node)
	}

	return codedom.VarDefinitionWithInit(name, initExpr, node)
}
Example #7
0
// scopeMemberAccessExpression scopes a member access expression in the SRG.
func (sb *scopeBuilder) scopeMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the child expression.
	childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	memberName := node.Get(parser.NodeMemberAccessIdentifier)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))

	switch childScope.GetKind() {

	case proto.ScopeKind_VALUE:
		childType := childScope.ResolvedTypeRef(sb.sg.tdg)
		if childType.IsNullable() {
			sb.decorateWithError(node, "Cannot access name '%v' under nullable type '%v'. Please use the ?. operator to ensure type safety.", memberName, childType)
			return newScope().Invalid().GetScope()
		}

		typeMember, rerr := childType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstance)
		if rerr != nil {
			sb.decorateWithError(node, "%v", rerr)
			return newScope().Invalid().GetScope()
		}

		memberScope := sb.getNamedScopeForMember(typeMember)
		context.staticDependencyCollector.checkNamedScopeForDependency(memberScope)

		return newScope().ForNamedScopeUnderType(memberScope, childType, context).GetScope()

	case proto.ScopeKind_GENERIC:
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		sb.decorateWithError(node, "Cannot attempt member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name())
		return newScope().Invalid().GetScope()

	case proto.ScopeKind_STATIC:
		staticType := childScope.StaticTypeRef(sb.sg.tdg)
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		memberScope, rerr := namedScope.ResolveStaticMember(memberName, module, staticType)
		if rerr != nil {
			sb.decorateWithError(node, "%v", rerr)
			return newScope().Invalid().GetScope()
		}
		return newScope().ForNamedScopeUnderType(memberScope, staticType, context).GetScope()

	default:
		panic("Unknown scope kind")
	}

	return newScope().Invalid().GetScope()
}
Example #8
0
// scopeStreamMemberAccessExpression scopes a stream member access expression in the SRG.
func (sb *scopeBuilder) scopeStreamMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the child expression.
	childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	memberName := node.Get(parser.NodeMemberAccessIdentifier)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))

	switch childScope.GetKind() {
	case proto.ScopeKind_VALUE:
		childType := childScope.ResolvedTypeRef(sb.sg.tdg)

		// Ensure the child type is a stream.
		generics, serr := childType.CheckConcreteSubtypeOf(sb.sg.tdg.StreamType())
		if serr != nil {
			sb.decorateWithError(node, "Cannot attempt stream access of name '%v' under non-stream type '%v': %v", memberName, childType, serr)
			return newScope().Invalid().GetScope()
		}

		valueType := generics[0]
		typeMember, rerr := valueType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstance)
		if rerr != nil {
			sb.decorateWithError(node, "%v", rerr)
			return newScope().Invalid().GetScope()
		}

		memberScope := sb.getNamedScopeForMember(typeMember)
		context.staticDependencyCollector.checkNamedScopeForDependency(memberScope)

		return newScope().ForNamedScopeUnderModifiedType(memberScope, valueType, makeStream, context).GetScope()

	case proto.ScopeKind_GENERIC:
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		sb.decorateWithError(node, "Cannot attempt stream member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name())
		return newScope().Invalid().GetScope()

	case proto.ScopeKind_STATIC:
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		sb.decorateWithError(node, "Cannot attempt stream member access of '%v' under %v %v, as it is a static type", memberName, namedScope.Title(), namedScope.Name())
		return newScope().Invalid().GetScope()

	default:
		panic("Unknown scope kind")
	}

	return newScope().Invalid().GetScope()
}
Example #9
0
// scopeNumericLiteralExpression scopes a numeric literal expression in the SRG.
func (sb *scopeBuilder) scopeNumericLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	numericValueStr := node.Get(parser.NodeNumericLiteralExpressionValue)
	_, isNotInt := strconv.ParseInt(numericValueStr, 0, 64)
	if isNotInt == nil {
		return newScope().
			Valid().
			Resolving(sb.sg.tdg.NewTypeReference(sb.sg.tdg.IntType())).
			GetScope()
	} else {
		return newScope().
			Valid().
			Resolving(sb.sg.tdg.NewTypeReference(sb.sg.tdg.FloatType())).
			GetScope()
	}
}
Example #10
0
// FindNameInScope finds the given name accessible from the scope under which the given node exists, if any.
func (g *SRG) FindNameInScope(name string, node compilergraph.GraphNode) (SRGScopeOrImport, bool) {
	// Attempt to resolve the name as pointing to a parameter, var statement, loop var or with var.
	srgNode, srgNodeFound := g.findAddedNameInScope(name, node)
	if srgNodeFound {
		return SRGNamedScope{srgNode, g}, true
	}

	// If still not found, try to resolve as a type or import.
	nodeSource := node.Get(parser.NodePredicateSource)
	parentModule, parentModuleFound := g.FindModuleBySource(compilercommon.InputSource(nodeSource))
	if !parentModuleFound {
		panic(fmt.Sprintf("Missing module for source %v", nodeSource))
	}

	// Try to resolve as a local member.
	srgTypeOrMember, typeOrMemberFound := parentModule.FindTypeOrMemberByName(name, ModuleResolveAll)
	if typeOrMemberFound {
		return SRGNamedScope{srgTypeOrMember.GraphNode, g}, true
	}

	// Try to resolve as an imported member.
	localImportNode, localImportFound := parentModule.findImportWithLocalName(name)
	if localImportFound {
		// Retrieve the package for the imported member.
		packageInfo := g.getPackageForImport(localImportNode)
		resolutionName := localImportNode.Get(parser.NodeImportPredicateSubsource)

		// If an SRG package, then continue with the resolution. Otherwise,
		// we return a named scope that says that the name needs to be furthered
		// resolved in the package by the type graph.
		if packageInfo.IsSRGPackage() {
			packageTypeOrMember, packagetypeOrMemberFound := packageInfo.FindTypeOrMemberByName(resolutionName)
			if packagetypeOrMemberFound {
				return SRGNamedScope{packageTypeOrMember.GraphNode, g}, true
			}
		}

		return SRGExternalPackageImport{packageInfo.packageInfo, resolutionName, g}, true
	}

	// Try to resolve as an imported package.
	importNode, importFound := parentModule.findImportByPackageName(name)
	if importFound {
		return SRGNamedScope{importNode, g}, true
	}

	return SRGNamedScope{}, false
}
Example #11
0
// scopeIdentifierExpression scopes an identifier expression in the SRG.
func (sb *scopeBuilder) scopeIdentifierExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	name := node.Get(parser.NodeIdentifierExpressionName)
	if name == ANONYMOUS_REFERENCE {
		// Make sure this node is under an assignment of some kind.
		var found = false
		for _, predicate := range ALLOWED_ANONYMOUS {
			if _, ok := node.TryGetIncomingNode(predicate); ok {
				found = true
				break
			}
		}

		if !found {
			sb.decorateWithError(node, "Anonymous identifier '_' cannot be used as a value")
			return newScope().Invalid().GetScope()
		}

		return newScope().ForAnonymousScope(sb.sg.tdg).GetScope()
	}

	// Lookup the name given, starting at the location of the current node.
	namedScope, rerr := sb.lookupNamedScope(name, node)
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().GetScope()
	}

	// Ensure that the named scope has a valid type.
	if !namedScope.IsValid(context) {
		return newScope().Invalid().GetScope()
	}

	// Warn if we are accessing an assignable value under an async function, as it will be executing
	// in a different context.
	if namedScope.IsAssignable() && namedScope.UnderModule() {
		srgImpl, found := context.getParentContainer(sb.sg.srg)
		if found && srgImpl.ContainingMember().IsAsyncFunction() {
			sb.decorateWithWarning(node, "%v '%v' is defined outside the async function and will therefore be unique for each call to this function", namedScope.Title(), name)
		}
	}

	context.staticDependencyCollector.checkNamedScopeForDependency(namedScope)
	return newScope().ForNamedScope(namedScope, context).GetScope()
}
Example #12
0
// FindReferencesInScope finds all identifier expressions that refer to the given name, under the given
// scope.
func (g *SRG) FindReferencesInScope(name string, node compilergraph.GraphNode) compilergraph.NodeIterator {
	// Note: This filter ensures that the name is accessible in the scope of the given node by checking that
	// the node referencing the name is contained by the given node.
	containingFilter := func(q compilergraph.GraphQuery) compilergraph.Query {
		startRune := node.GetValue(parser.NodePredicateStartRune).Int()
		endRune := node.GetValue(parser.NodePredicateEndRune).Int()

		return q.
			HasWhere(parser.NodePredicateStartRune, compilergraph.WhereGT, startRune).
			HasWhere(parser.NodePredicateEndRune, compilergraph.WhereLT, endRune)
	}

	return g.layer.StartQuery(name).
		In(parser.NodeIdentifierExpressionName).
		IsKind(parser.NodeTypeIdentifierExpression).
		Has(parser.NodePredicateSource, node.Get(parser.NodePredicateSource)).
		FilterBy(containingFilter).
		BuildNodeIterator()
}
Example #13
0
// scopeStructuralNewExpressionEntry scopes a single entry in a structural new expression.
func (sb *scopeBuilder) scopeStructuralNewExpressionEntry(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	parentNode := node.GetIncomingNode(parser.NodeStructuralNewExpressionChildEntry)
	parentExprScope := sb.getScope(parentNode.GetNode(parser.NodeStructuralNewTypeExpression), context)

	parentType := parentExprScope.StaticTypeRef(sb.sg.tdg)
	if parentExprScope.GetKind() == proto.ScopeKind_VALUE {
		parentType = parentExprScope.ResolvedTypeRef(sb.sg.tdg)
	}

	entryName := node.Get(parser.NodeStructuralNewEntryKey)

	// Get the scope for the value.
	valueScope := sb.getScope(node.GetNode(parser.NodeStructuralNewEntryValue), context)
	if !valueScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Lookup the member associated with the entry name.
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	member, rerr := parentType.ResolveAccessibleMember(entryName, module, typegraph.MemberResolutionInstance)
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().GetScope()
	}

	// Ensure the member is assignable.
	if member.IsReadOnly() {
		sb.decorateWithError(node, "%v %v under type %v is read-only", member.Title(), member.Name(), parentType)
		return newScope().Invalid().GetScope()
	}

	// Get the member's assignable type, transformed under the parent type, and ensure it is assignable
	// from the type of the value.
	assignableType := member.AssignableType().TransformUnder(parentType)
	valueType := valueScope.ResolvedTypeRef(sb.sg.tdg)

	if aerr := valueType.CheckSubTypeOf(assignableType); aerr != nil {
		sb.decorateWithError(node, "Cannot assign value of type %v to %v %v: %v", valueType, member.Title(), member.Name(), aerr)
		return newScope().Invalid().GetScope()
	}

	return newScope().ForNamedScope(sb.getNamedScopeForMember(member), context).Valid().GetScope()
}
Example #14
0
// scopeBinaryExpression scopes a binary expression in the SRG.
func (sb *scopeBuilder) scopeBinaryExpression(node compilergraph.GraphNode, opName string, context scopeContext) *scopeInfoBuilder {
	// Get the scope of the left and right expressions.
	leftScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionLeftExpr), context)
	rightScope := sb.getScope(node.GetNode(parser.NodeBinaryExpressionRightExpr), context)

	// Ensure that both scopes are valid.
	if !leftScope.GetIsValid() || !rightScope.GetIsValid() {
		return newScope().Invalid()
	}

	// Ensure that both scopes have the same type.
	leftType := leftScope.ResolvedTypeRef(sb.sg.tdg)
	rightType := rightScope.ResolvedTypeRef(sb.sg.tdg)

	if leftType != rightType {
		sb.decorateWithError(node, "Operator '%v' requires operands of the same type. Found: '%v' and '%v'", opName, leftType, rightType)
		return newScope().Invalid()
	}

	// Ensure that the operator exists under the resolved type.
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	operator, rerr := leftType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator)
	if rerr != nil {
		sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, leftType)
		return newScope().Invalid()
	}

	returnType, _ := operator.ReturnType()

	// Check for nullable values.
	if leftType.NullValueAllowed() {
		sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, leftType)
		return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType))
	}

	if rightType.NullValueAllowed() {
		sb.decorateWithError(node, "Cannot invoke operator '%v' on nullable type '%v'", opName, rightType)
		return newScope().Invalid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType))
	}

	return newScope().Valid().CallsOperator(operator).Resolving(returnType.TransformUnder(leftType))
}
Example #15
0
// scopeStructuralNewTypeExpression scopes a structural new expression for constructing a new instance
// of a structural or class type.
func (sb *scopeBuilder) scopeStructuralNewTypeExpression(node compilergraph.GraphNode, childScope *proto.ScopeInfo, context scopeContext) proto.ScopeInfo {
	// Retrieve the static type.
	staticTypeRef := childScope.StaticTypeRef(sb.sg.tdg)

	// Ensure that the static type is a struct OR it is a class with an accessible 'new'.
	staticType := staticTypeRef.ReferredType()
	switch staticType.TypeKind() {
	case typegraph.ClassType:
		// Classes can only be constructed structurally if they are in the same module as this call.
		// Otherwise, an exported constructor must be used.
		module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
		_, rerr := staticTypeRef.ResolveAccessibleMember("new", module, typegraph.MemberResolutionStatic)
		if rerr != nil {
			sb.decorateWithError(node, "Cannot structurally construct type %v, as it is imported from another module", staticTypeRef)
			return newScope().Invalid().Resolving(staticTypeRef).GetScope()
		}

	case typegraph.StructType:
		// Structs can be constructed by anyone, assuming that their members are all exported.
		// That check occurs below.
		break

	default:
		sb.decorateWithError(node, "Cannot structurally construct type %v", staticTypeRef)
		return newScope().Invalid().Resolving(staticTypeRef).GetScope()
	}

	encountered, isValid := sb.scopeStructuralNewEntries(node, context)
	if !isValid {
		return newScope().Invalid().Resolving(staticTypeRef).GetScope()
	}

	// Ensure that all required entries are present.
	for _, field := range staticType.RequiredFields() {
		if _, ok := encountered[field.Name()]; !ok {
			isValid = false
			sb.decorateWithError(node, "Non-nullable %v '%v' is required to construct type %v", field.Title(), field.Name(), staticTypeRef)
		}
	}

	return newScope().IsValid(isValid).Resolving(staticTypeRef).GetScope()
}
Example #16
0
// scopeSmlNormalAttribute scopes an SML expression attribute under a declaration.
func (sb *scopeBuilder) scopeSmlAttribute(node compilergraph.GraphNode, propsType typegraph.TypeReference, context scopeContext) (string, bool) {
	attributeName := node.Get(parser.NodeSmlAttributeName)

	// If the props type is a struct or class, ensure that the attribute name exists.
	var allowedValueType = sb.sg.tdg.AnyTypeReference()
	if propsType.IsRefToStruct() || propsType.IsRefToClass() {
		module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
		resolvedMember, rerr := propsType.ResolveAccessibleMember(attributeName, module, typegraph.MemberResolutionInstance)
		if rerr != nil {
			sb.decorateWithError(node, "%v", rerr)
			return attributeName, false
		}

		allowedValueType = resolvedMember.AssignableType()
	} else {
		// The props type must be a mapping, so the value must match it value type.
		allowedValueType = propsType.Generics()[0]
	}

	// Scope the attribute value (if any). If none, then we default to a boolean value.
	var attributeValueType = sb.sg.tdg.BoolTypeReference()

	valueNode, hasValueNode := node.TryGetNode(parser.NodeSmlAttributeValue)
	if hasValueNode {
		attributeValueScope := sb.getScope(valueNode, context)
		if !attributeValueScope.GetIsValid() {
			return attributeName, false
		}

		attributeValueType = attributeValueScope.ResolvedTypeRef(sb.sg.tdg)
	}

	// Ensure it matches the assignable value type.
	if serr := attributeValueType.CheckSubTypeOf(allowedValueType); serr != nil {
		sb.decorateWithError(node, "Cannot assign value of type %v for attribute %v: %v", attributeValueType, attributeName, serr)
		return attributeName, false
	}

	return attributeName, true
}
Example #17
0
// getPackageForImport returns the package information for the package imported by the given import
// package node.
func (g *SRG) getPackageForImport(importPackageNode compilergraph.GraphNode) importedPackage {
	importNode := importPackageNode.GetIncomingNode(parser.NodeImportPredicatePackageRef)

	// Note: There may not be a kind, in which case this will return empty string, which is the
	// default kind.
	packageKind, _ := importNode.TryGet(parser.NodeImportPredicateKind)
	packageLocation := importNode.Get(parser.NodeImportPredicateLocation)

	packageInfo, ok := g.packageMap.Get(packageKind, packageLocation)
	if !ok {
		source := importNode.Get(parser.NodeImportPredicateSource)
		subsource, _ := importPackageNode.TryGet(parser.NodeImportPredicateSubsource)
		panic(fmt.Sprintf("Missing package info for import %s %s (reference %v) (node %v)\nPackage Map: %v",
			source, subsource, packageLocation, importNode, g.packageMap))
	}

	return importedPackage{
		srg:          g,
		packageInfo:  packageInfo,
		importSource: compilercommon.InputSource(importPackageNode.Get(parser.NodePredicateSource)),
	}
}
Example #18
0
// scopeDeclaredValue scopes a declared value (variable statement, variable member, type field).
func (sb *scopeBuilder) scopeDeclaredValue(node compilergraph.GraphNode, title string, option requiresInitializerOption, exprPredicate compilergraph.Predicate, context scopeContext) proto.ScopeInfo {
	var exprScope *proto.ScopeInfo = nil

	exprNode, hasExpression := node.TryGetNode(exprPredicate)
	if hasExpression {
		// Scope the expression.
		exprScope = sb.getScope(exprNode, context)
		if !exprScope.GetIsValid() {
			return newScope().Invalid().GetScope()
		}
	}

	// Load the declared type, if any.
	declaredType, hasDeclaredType := sb.getDeclaredVariableType(node)
	if !hasDeclaredType {
		if exprScope == nil {
			panic("Somehow ended up with no declared type and no expr scope")
		}

		return newScope().Valid().AssignableResolvedTypeOf(exprScope).GetScope()
	}

	// Compare against the type of the expression.
	if hasExpression {
		exprType := exprScope.ResolvedTypeRef(sb.sg.tdg)
		if serr := exprType.CheckSubTypeOf(declaredType); serr != nil {
			sb.decorateWithError(node, "%s '%s' has declared type '%v': %v", title, node.Get(parser.NodeVariableStatementName), declaredType, serr)
			return newScope().Invalid().GetScope()
		}
	} else if option == requiresInitializer {
		// Make sure if the type is non-nullable that there is an expression.
		if !declaredType.IsNullable() {
			sb.decorateWithError(node, "%s '%s' must have explicit initializer as its type '%v' is non-nullable", title, node.Get(parser.NodeVariableStatementName), declaredType)
			return newScope().Invalid().Assignable(declaredType).GetScope()
		}
	}

	return newScope().Valid().Assignable(declaredType).GetScope()
}
Example #19
0
// buildNumericLiteral builds the CodeDOM for a numeric literal.
func (db *domBuilder) buildNumericLiteral(node compilergraph.GraphNode) codedom.Expression {
	numericValueStr := node.Get(parser.NodeNumericLiteralExpressionValue)
	if strings.HasSuffix(numericValueStr, "f") {
		numericValueStr = numericValueStr[0 : len(numericValueStr)-1]
	}

	// Handle binary.
	if strings.HasPrefix(numericValueStr, "0b") || strings.HasPrefix(numericValueStr, "0B") {
		parsed, _ := strconv.ParseInt(numericValueStr[2:], 2, 64)
		numericValueStr = strconv.Itoa(int(parsed))
	}

	// Note: Handles Hex.
	intValue, isNotInt := strconv.ParseInt(numericValueStr, 0, 64)
	if isNotInt == nil {
		numericValueStr = strconv.Itoa(int(intValue))
	}

	exprScope, _ := db.scopegraph.GetScope(node)
	numericType := exprScope.ResolvedTypeRef(db.scopegraph.TypeGraph()).ReferredType()
	return codedom.NominalWrapping(codedom.LiteralValue(numericValueStr, node), numericType, node)
}
Example #20
0
// scopeSliceChildExpression scopes the child expression of a slice expression, returning whether it
// is valid and the associated operator found, if any.
func (sb *scopeBuilder) scopeSliceChildExpression(node compilergraph.GraphNode, opName string, context scopeContext) (typegraph.TGMember, typegraph.TypeReference, bool) {
	// Scope the child expression of the slice.
	childScope := sb.getScope(node.GetNode(parser.NodeSliceExpressionChildExpr), context)
	if !childScope.GetIsValid() {
		return typegraph.TGMember{}, sb.sg.tdg.AnyTypeReference(), false
	}

	childType := childScope.ResolvedTypeRef(sb.sg.tdg)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
	operator, rerr := childType.ResolveAccessibleMember(opName, module, typegraph.MemberResolutionOperator)
	if rerr != nil {
		sb.decorateWithError(node, "Operator '%v' is not defined on type '%v'", opName, childType)
		return typegraph.TGMember{}, childType, false
	}

	// Ensure that the child expression is not nullable.
	if childType.NullValueAllowed() {
		sb.decorateWithError(node, "Operator '%v' cannot be called on nullable type '%v'", opName, childType)
		return typegraph.TGMember{}, childType, false
	}

	return operator, childType, true
}
Example #21
0
// buildLocalizedRefValue returns a string value for representing a localized type reference.
func buildLocalizedRefValue(genericNode compilergraph.GraphNode) string {
	var buffer bytes.Buffer

	genericKind := genericNode.Get(NodePredicateGenericKind)
	genericIndex := genericNode.Get(NodePredicateGenericIndex)

	localId := "local:" + genericKind + ":" + genericIndex

	// Referenced type ID. For localized types, this is the generic index and kind.
	buffer.WriteByte('[')
	buffer.WriteString(localId)
	buffer.WriteString(strings.Repeat(" ", trhSlotTypeId.length-len(localId)))
	buffer.WriteByte(']')

	// Flags: special and nullable.
	buffer.WriteByte(specialFlagLocal)
	buffer.WriteByte(nullableFlagFalse)

	// Generic count and parameter count.
	buffer.WriteString(padNumberToString(0, trhSlotGenericCount.length))
	buffer.WriteString(padNumberToString(0, trhSlotParameterCount.length))

	return buffer.String()
}
Example #22
0
// ResolutionPath returns the full resolution path for this type reference.
// Panics if this is not a RefKind of TypeRefPath.
func (t SRGTypeRef) ResolutionPath() string {
	compilerutil.DCHECK(func() bool { return t.RefKind() == TypeRefPath }, "Expected type ref path")

	var resolvePathPieces = make([]string, 0)
	var currentPath compilergraph.GraphNode = t.GraphNode.
		GetNode(parser.NodeTypeReferencePath).
		GetNode(parser.NodeIdentifierPathRoot)

	for {
		// Add the path piece to the array.
		name := currentPath.Get(parser.NodeIdentifierAccessName)
		resolvePathPieces = append([]string{name}, resolvePathPieces...)

		// If there is a source, continue searching.
		source, found := currentPath.TryGetNode(parser.NodeIdentifierAccessSource)
		if !found {
			break
		}

		currentPath = source
	}

	return strings.Join(resolvePathPieces, ".")
}
Example #23
0
// scopeDynamicMemberAccessExpression scopes a dynamic member access expression in the SRG.
func (sb *scopeBuilder) scopeDynamicMemberAccessExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the child expression.
	childScope := sb.getScope(node.GetNode(parser.NodeMemberAccessChildExpr), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	memberName := node.Get(parser.NodeMemberAccessIdentifier)
	module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))

	scopeMemberAccess := func(childType typegraph.TypeReference, expectStatic bool) proto.ScopeInfo {
		// If the child type is any, then this operator returns another value of any, regardless of name.
		if childType.IsAny() {
			return newScope().Valid().Resolving(sb.sg.tdg.AnyTypeReference()).GetScope()
		}

		var lookupType = childType
		if childType.IsNullable() {
			lookupType = childType.AsNonNullable()
		}

		// Look for the matching type member, either instance or static. If not found, then the access
		// returns an "any" type.
		typeMember, rerr := lookupType.ResolveAccessibleMember(memberName, module, typegraph.MemberResolutionInstanceOrStatic)
		if rerr != nil {
			return newScope().Valid().Resolving(sb.sg.tdg.AnyTypeReference()).GetScope()
		}

		// Ensure static isn't accessed under instance and vice versa.
		if typeMember.IsStatic() != expectStatic {
			if typeMember.IsStatic() {
				sb.decorateWithError(node, "Member '%v' is static but accessed under an instance value", typeMember.Name())
			} else {
				sb.decorateWithError(node, "Member '%v' is non-static but accessed under a static value", typeMember.Name())
			}
			return newScope().Invalid().GetScope()
		}

		// The resulting type (if matching a named scope) is the named scope, but also nullable (since the operator)
		// allows for nullable types.
		memberScope := sb.getNamedScopeForMember(typeMember)
		context.staticDependencyCollector.checkNamedScopeForDependency(memberScope)

		if childType.IsNullable() {
			sb.decorateWithWarning(node, "Dynamic access of known member '%v' under type %v. The ?. operator is suggested.", typeMember.Name(), childType)
			return newScope().ForNamedScopeUnderModifiedType(memberScope, lookupType, makeNullable, context).GetScope()
		} else {
			sb.decorateWithWarning(node, "Dynamic access of known member '%v' under type %v. The . operator is suggested.", typeMember.Name(), childType)
			return newScope().ForNamedScopeUnderType(memberScope, lookupType, context).GetScope()
		}
	}

	switch childScope.GetKind() {
	case proto.ScopeKind_VALUE:
		childType := childScope.ResolvedTypeRef(sb.sg.tdg)
		return scopeMemberAccess(childType, false)

	case proto.ScopeKind_STATIC:
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		if !namedScope.IsType() {
			sb.decorateWithError(node, "Cannot attempt dynamic member access of '%v' under %v %v, as it is not a type", memberName, namedScope.Title(), namedScope.Name())
			return newScope().Invalid().GetScope()
		}

		childType := namedScope.StaticType(context)
		return scopeMemberAccess(childType, true)

	case proto.ScopeKind_GENERIC:
		namedScope, _ := sb.getNamedScopeForScope(childScope)
		sb.decorateWithError(node, "Cannot attempt dynamic member access of '%v' under %v %v, as it is generic without specification", memberName, namedScope.Title(), namedScope.Name())
		return newScope().Invalid().GetScope()

	default:
		panic("Unknown scope kind")
	}

	return newScope().Invalid().GetScope()
}
Example #24
0
// scopeSwitchStatement scopes a switch statement in the SRG.
func (sb *scopeBuilder) scopeSwitchStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Check for an expression. If a switch has an expression, then all cases must match against it.
	var isValid = true
	var switchValueType = sb.sg.tdg.BoolTypeReference()

	exprNode, hasExpression := node.TryGetNode(parser.NodeSwitchStatementExpression)
	if hasExpression {
		exprScope := sb.getScope(exprNode, context)
		if exprScope.GetIsValid() {
			switchValueType = exprScope.ResolvedTypeRef(sb.sg.tdg)
		} else {
			isValid = false
		}
	}

	// Ensure that the switch type has a defined accessible comparison operator.
	if isValid {
		module := compilercommon.InputSource(node.Get(parser.NodePredicateSource))
		_, rerr := switchValueType.ResolveAccessibleMember("equals", module, typegraph.MemberResolutionOperator)
		if rerr != nil {
			sb.decorateWithError(node, "Cannot switch over instance of type '%v', as it does not define or export an 'equals' operator", switchValueType)
			isValid = false
		}
	}

	// Scope each of the case statements under the switch.
	var returnedType = sb.sg.tdg.VoidTypeReference()
	var settlesScope = true
	var hasDefault = false
	var labelSet = newLabelSet()

	sit := node.StartQuery().
		Out(parser.NodeSwitchStatementCase).
		BuildNodeIterator()

	for sit.Next() {
		// Scope the statement block under the case.
		statementBlockNode := sit.Node().GetNode(parser.NodeSwitchStatementCaseStatement)
		statementBlockScope := sb.getScope(statementBlockNode, context.withBreakable(node))
		if !statementBlockScope.GetIsValid() {
			isValid = false
		}

		labelSet.AppendLabelsOf(statementBlockScope)

		returnedType = returnedType.Intersect(statementBlockScope.ReturnedTypeRef(sb.sg.tdg))
		settlesScope = settlesScope && statementBlockScope.GetIsSettlingScope()

		// Check the case's expression (if any) against the type expected.
		caseExprNode, hasCaseExpression := sit.Node().TryGetNode(parser.NodeSwitchStatementCaseExpression)
		if hasCaseExpression {
			caseExprScope := sb.getScope(caseExprNode, context)
			if caseExprScope.GetIsValid() {
				caseExprType := caseExprScope.ResolvedTypeRef(sb.sg.tdg)
				if serr := caseExprType.CheckSubTypeOf(switchValueType); serr != nil {
					sb.decorateWithError(node, "Switch cases must have values matching type '%v': %v", switchValueType, serr)
					isValid = false
				}
			} else {
				isValid = false
			}
		} else {
			hasDefault = true
		}
	}

	// If there isn't a default case, then the switch cannot be known to return in all cases.
	if !hasDefault {
		returnedType = sb.sg.tdg.VoidTypeReference()
		settlesScope = false
	}

	return newScope().IsValid(isValid).Returning(returnedType, settlesScope).WithLabelSet(labelSet).GetScope()
}
Example #25
0
// salForNode returns a SourceAndLocation for the given graph node.
func salForNode(node compilergraph.GraphNode) compilercommon.SourceAndLocation {
	return salForValues(node.Get(parser.NodePredicateSource), node.Get(parser.NodePredicateStartRune))
}
Example #26
0
// salForNode returns a SourceAndLocation for the given graph node.
func salForNode(node compilergraph.GraphNode) compilercommon.SourceAndLocation {
	return compilercommon.NewSourceAndLocation(
		compilercommon.InputSource(node.Get(parser.NodePredicateSource)),
		node.GetValue(parser.NodePredicateStartRune).Int())
}
Example #27
0
// buildMemberAccessExpression builds the CodeDOM for a member access expression.
func (db *domBuilder) buildMemberAccessExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := node.GetNode(parser.NodeMemberAccessChildExpr)
	return db.buildNamedAccess(node, node.Get(parser.NodeMemberAccessIdentifier), &childExpr)
}
Example #28
0
// findAddedNameInScope finds the {parameter, with, loop, var} node exposing the given name, if any.
func (g *SRG) findAddedNameInScope(name string, node compilergraph.GraphNode) (compilergraph.GraphNode, bool) {
	nodeSource := node.Get(parser.NodePredicateSource)
	nodeStartIndex := node.GetValue(parser.NodePredicateStartRune).Int()

	// Note: This filter ensures that the name is accessible in the scope of the given node by checking that
	// the node adding the name contains the given node.
	containingFilter := func(q compilergraph.GraphQuery) compilergraph.Query {
		startRune := node.GetValue(parser.NodePredicateStartRune).Int()
		endRune := node.GetValue(parser.NodePredicateEndRune).Int()

		return q.
			In(parser.NodePredicateTypeMemberParameter,
				parser.NodeLambdaExpressionInferredParameter,
				parser.NodeLambdaExpressionParameter,
				parser.NodePredicateTypeMemberGeneric,
				parser.NodeStatementNamedValue,
				parser.NodeAssignedDestination,
				parser.NodeAssignedRejection,
				parser.NodePredicateChild,
				parser.NodeStatementBlockStatement).
			InIfKind(parser.NodeStatementBlockStatement, parser.NodeTypeResolveStatement).
			HasWhere(parser.NodePredicateStartRune, compilergraph.WhereLTE, startRune).
			HasWhere(parser.NodePredicateEndRune, compilergraph.WhereGTE, endRune)
	}

	nit := g.layer.StartQuery(name).
		In("named").
		Has(parser.NodePredicateSource, nodeSource).
		IsKind(parser.NodeTypeParameter, parser.NodeTypeNamedValue, parser.NodeTypeAssignedValue,
			parser.NodeTypeVariableStatement, parser.NodeTypeLambdaParameter, parser.NodeTypeGeneric).
		FilterBy(containingFilter).
		BuildNodeIterator(parser.NodePredicateStartRune, parser.NodePredicateEndRune)

	// Sort the nodes found by location and choose the closest node.
	var results = make(scopeResultNodes, 0)
	for nit.Next() {
		node := nit.Node()
		startIndex := nit.GetPredicate(parser.NodePredicateStartRune).Int()

		// If the node is a variable statement or assigned value, we have do to additional checks
		// (since they are not block scoped but rather statement scoped).
		if node.Kind() == parser.NodeTypeVariableStatement || node.Kind() == parser.NodeTypeAssignedValue {
			endIndex := nit.GetPredicate(parser.NodePredicateEndRune).Int()
			if node.Kind() == parser.NodeTypeAssignedValue {
				if parentNode, ok := node.TryGetIncomingNode(parser.NodeAssignedDestination); ok {
					endIndex = parentNode.GetValue(parser.NodePredicateEndRune).Int()
				} else if parentNode, ok := node.TryGetIncomingNode(parser.NodeAssignedRejection); ok {
					endIndex = parentNode.GetValue(parser.NodePredicateEndRune).Int()
				} else {
					panic("Missing assigned parent")
				}
			}

			// Check that the startIndex of the variable statement is <= the startIndex of the parent node
			if startIndex > nodeStartIndex {
				continue
			}

			// Ensure that the scope starts after the end index of the variable. Otherwise, the variable
			// name could be used in its initializer expression (which is expressly disallowed).
			if nodeStartIndex <= endIndex {
				continue
			}
		}

		results = append(results, scopeResultNode{node, startIndex})
	}

	if len(results) == 1 {
		// If there is a single result, return it.
		return results[0].node, true
	} else if len(results) > 1 {
		// Otherwise, sort the list by startIndex and choose the one closest to the scope node.
		sort.Sort(results)
		return results[0].node, true
	}

	return compilergraph.GraphNode{}, false
}
Example #29
0
// buildIdentifierExpression builds the CodeDOM for an identifier expression.
func (db *domBuilder) buildIdentifierExpression(node compilergraph.GraphNode) codedom.Expression {
	return db.buildNamedAccess(node, node.Get(parser.NodeIdentifierExpressionName), nil)
}
Example #30
0
// buildBooleanLiteral builds the CodeDOM for a boolean literal.
func (db *domBuilder) buildBooleanLiteral(node compilergraph.GraphNode) codedom.Expression {
	booleanValueStr := node.Get(parser.NodeBooleanLiteralExpressionValue)
	return codedom.NominalWrapping(codedom.LiteralValue(booleanValueStr, node), db.scopegraph.TypeGraph().BoolType(), node)
}