Example #1
0
// scopeAssignStatement scopes a assign statement in the SRG.
func (sb *scopeBuilder) scopeAssignStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// TODO: Handle tuple assignment once we figure out tuple types

	// Scope the name.
	nameScope := sb.getScope(node.GetNode(parser.NodeAssignStatementName), context.withAccess(scopeSetAccess))

	// Scope the expression value.
	exprScope := sb.getScope(node.GetNode(parser.NodeAssignStatementValue), context)

	if !nameScope.GetIsValid() || !exprScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Check that we have a named item.
	namedScopedRef, found := sb.getNamedScopeForScope(nameScope)
	if !found {
		sb.decorateWithError(node, "Cannot assign to non-named value")
		return newScope().Invalid().GetScope()
	}

	// Check that the item is assignable.
	if !namedScopedRef.IsAssignable() {
		sb.decorateWithError(node, "Cannot assign to non-assignable %v %v", namedScopedRef.Title(), namedScopedRef.Name())
		return newScope().Invalid().GetScope()
	}

	// Ensure that we can assign the expr value to the named scope.
	if serr := exprScope.ResolvedTypeRef(sb.sg.tdg).CheckSubTypeOf(nameScope.AssignableTypeRef(sb.sg.tdg)); serr != nil {
		sb.decorateWithError(node, "Cannot assign value to %v %v: %v", namedScopedRef.Title(), namedScopedRef.Name(), serr)
		return newScope().Invalid().GetScope()
	}

	return newScope().Valid().GetScope()
}
Example #2
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 #3
0
// scopeIndexerExpression scopes an indexer expression (slice with single numerical index) in the SRG.
func (sb *scopeBuilder) scopeIndexerExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Lookup the indexing operator.
	var opName = "index"
	if context.accessOption == scopeSetAccess {
		opName = "setindex"
	}

	operator, childType, found := sb.scopeSliceChildExpression(node, opName, context)
	if !found {
		return newScope().Invalid().GetScope()
	}

	// Scope the index expression.
	exprScope := sb.getScope(node.GetNode(parser.NodeSliceExpressionIndex), context)
	if !exprScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Ensure the index expression type matches that expected.
	exprType := exprScope.ResolvedTypeRef(sb.sg.tdg)
	parameterType := operator.ParameterTypes()[0].TransformUnder(childType)

	if serr := exprType.CheckSubTypeOf(parameterType); serr != nil {
		sb.decorateWithError(node, "Indexer parameter must be type %v: %v", parameterType, serr)
		return newScope().Invalid().GetScope()
	}

	if context.accessOption == scopeSetAccess {
		return newScope().Valid().ForNamedScopeUnderType(sb.getNamedScopeForMember(operator), childType, context).GetScope()
	} else {
		returnType, _ := operator.ReturnType()
		return newScope().Valid().CallsOperator(operator).Resolving(returnType.TransformUnder(childType)).GetScope()
	}
}
Example #4
0
// buildResolveStatement builds the CodeDOM for a resolve statement.
func (db *domBuilder) buildResolveStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	sourceExpr := db.getExpression(node, parser.NodeResolveStatementSource)

	destinationNode := node.GetNode(parser.NodeAssignedDestination)
	destinationScope, _ := db.scopegraph.GetScope(destinationNode)
	destinationName := destinationNode.Get(parser.NodeNamedValueName)

	var destinationStatement codedom.Statement = nil
	if !destinationScope.GetIsAnonymousReference() {
		destinationStatement = codedom.VarDefinitionWithInit(destinationName, sourceExpr, node)
	} else {
		destinationStatement = codedom.ExpressionStatement(sourceExpr, node)
		destinationName = ""
	}

	// If the resolve statement has a rejection value, then we need to wrap the source expression
	// call to catch any rejections *or* exceptions.
	rejectionNode, hasRejection := node.TryGetNode(parser.NodeAssignedRejection)
	if hasRejection {
		rejectionName := rejectionNode.Get(parser.NodeNamedValueName)
		rejectionScope, _ := db.scopegraph.GetScope(rejectionNode)
		if rejectionScope.GetIsAnonymousReference() {
			rejectionName = ""
		}

		empty := codedom.EmptyStatement(node)
		return codedom.ResolveExpression(sourceExpr, destinationName, rejectionName, empty, node), empty
	} else {
		// Otherwise, we simply execute the expression, optionally assigning it to the
		// destination variable.
		return destinationStatement, destinationStatement
	}
}
Example #5
0
// scopeInlineLambaExpression scopes an inline lambda expression node in the SRG.
func (sb *scopeBuilder) scopeInlineLambaExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var returnType = sb.sg.tdg.AnyTypeReference()

	// Scope the lambda's internal expression.
	exprScope := sb.getScope(node.GetNode(parser.NodeLambdaExpressionChildExpr), context)
	if exprScope.GetIsValid() {
		returnType = exprScope.ResolvedTypeRef(sb.sg.tdg)
	}

	// Build the function type.
	var functionType = sb.sg.tdg.FunctionTypeReference(returnType)

	// Add the parameter types.
	pit := node.StartQuery().
		Out(parser.NodeLambdaExpressionInferredParameter).
		BuildNodeIterator()

	for pit.Next() {
		parameterType, hasParameterType := sb.inferredParameterTypes.Get(string(pit.Node().NodeId))
		if hasParameterType {
			functionType = functionType.WithParameter(parameterType.(typegraph.TypeReference))
		} else {
			functionType = functionType.WithParameter(sb.sg.tdg.AnyTypeReference())
		}
	}

	return newScope().IsValid(exprScope.GetIsValid()).Resolving(functionType).GetScope()
}
Example #6
0
// buildCastExpression builds the CodeDOM for a cast expression.
func (db *domBuilder) buildCastExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeCastExpressionChildExpr)

	// Determine the resulting type.
	scope, _ := db.scopegraph.GetScope(node)
	resultingType := scope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	// If the resulting type is a structural subtype of the child expression's type, then
	// we are accessing the automatically composited inner instance.
	childScope, _ := db.scopegraph.GetScope(node.GetNode(parser.NodeCastExpressionChildExpr))
	childType := childScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	if childType.CheckStructuralSubtypeOf(resultingType) {
		return codedom.NestedTypeAccess(childExpr, resultingType, node)
	}

	// Otherwise, add a cast call with the cast type.
	typeLiteral := codedom.TypeLiteral(resultingType, node)
	allowNull := codedom.LiteralValue("false", node)
	if resultingType.NullValueAllowed() {
		allowNull = codedom.LiteralValue("true", node)
	}

	return codedom.RuntimeFunctionCall(codedom.CastFunction, []codedom.Expression{childExpr, typeLiteral, allowNull}, node)
}
Example #7
0
// buildArrowStatement builds the CodeDOM for an arrow statement.
func (db *domBuilder) buildArrowStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	sourceExpr := codedom.RuntimeFunctionCall(
		codedom.TranslatePromiseFunction,
		[]codedom.Expression{db.getExpression(node, parser.NodeArrowStatementSource)},
		node)

	destinationNode := node.GetNode(parser.NodeArrowStatementDestination)
	destinationScope, _ := db.scopegraph.GetScope(destinationNode)

	var destinationTarget codedom.Expression = nil
	var rejectionTarget codedom.Expression = nil

	// Retrieve the expression of the destination variable.
	if !destinationScope.GetIsAnonymousReference() {
		destinationTarget = db.buildAssignmentExpression(destinationNode, codedom.LocalReference("resolved", node), node)
	}

	// Retrieve the expression of the rejection variable.
	rejectionNode, hasRejection := node.TryGetNode(parser.NodeArrowStatementRejection)
	if hasRejection {
		rejectionScope, _ := db.scopegraph.GetScope(rejectionNode)
		if !rejectionScope.GetIsAnonymousReference() {
			rejectionTarget = db.buildAssignmentExpression(rejectionNode, codedom.LocalReference("rejected", node), node)
		}
	}

	empty := codedom.EmptyStatement(node)
	promise := codedom.ArrowPromise(sourceExpr, destinationTarget, rejectionTarget, empty, node)
	return promise, empty
}
Example #8
0
// buildStructuralNewExpression builds the CodeDOM for a structural new expression.
func (db *domBuilder) buildStructuralNewExpression(node compilergraph.GraphNode) codedom.Expression {
	// Collect the full set of initializers, by member.
	initializers := map[string]codedom.Expression{}
	eit := node.StartQuery().
		Out(parser.NodeStructuralNewExpressionChildEntry).
		BuildNodeIterator()

	for eit.Next() {
		entryScope, _ := db.scopegraph.GetScope(eit.Node())
		entryName, _ := db.scopegraph.GetReferencedName(entryScope)
		entryMember, _ := entryName.Member()

		initializers[entryMember.Name()] = db.getExpression(eit.Node(), parser.NodeStructuralNewEntryValue)
	}

	childScope, _ := db.scopegraph.GetScope(node.GetNode(parser.NodeStructuralNewTypeExpression))

	nodeScope, _ := db.scopegraph.GetScope(node)
	if nodeScope.HasLabel(proto.ScopeLabel_STRUCTURAL_UPDATE_EXPR) {
		// Build a call to the Clone() method followed by assignments.
		resolvedTypeRef := childScope.ResolvedTypeRef(db.scopegraph.TypeGraph())
		return db.buildStructCloneExpression(resolvedTypeRef, initializers, node)
	} else {
		// Build a call to the new() constructor of the type with the required field expressions.
		staticTypeRef := childScope.StaticTypeRef(db.scopegraph.TypeGraph())
		return db.buildStructInitializerExpression(staticTypeRef, initializers, node)
	}
}
Example #9
0
// scopeRootTypeExpression scopes a root-type expression in the SRG.
func (sb *scopeBuilder) scopeRootTypeExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the sub expression.
	childScope := sb.getScope(node.GetNode(parser.NodeUnaryExpressionChildExpr), context)

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

	childType := childScope.ResolvedTypeRef(sb.sg.tdg)

	// Ensure the child type is not void.
	if childType.IsVoid() || childType.IsNull() {
		sb.decorateWithError(node, "Root type operator (&) cannot be applied to value of type %v", childType)
		return newScope().Invalid().GetScope()
	}

	// Ensure the child type is nominal, interface or any.
	if !childType.IsAny() {
		referredType := childType.ReferredType()
		if referredType.TypeKind() == typegraph.NominalType {
			// The result of the operator is the nominal type's parent type.
			return newScope().Valid().Resolving(referredType.ParentTypes()[0]).GetScope()
		}

		if referredType.TypeKind() != typegraph.ImplicitInterfaceType && referredType.TypeKind() != typegraph.GenericType {
			sb.decorateWithError(node, "Root type operator (&) cannot be applied to value of type %v", childType)
			return newScope().Invalid().GetScope()
		}
	}

	// The result of the operator is a value of any type.
	return newScope().Valid().Resolving(sb.sg.tdg.AnyTypeReference()).GetScope()
}
Example #10
0
// buildBinaryOperatorExpression builds the CodeDOM for a binary operator.
func (db *domBuilder) buildBinaryOperatorExpression(node compilergraph.GraphNode, modifier exprModifier) codedom.Expression {
	scope, _ := db.scopegraph.GetScope(node)
	operator, _ := scope.CalledOperator(db.scopegraph.TypeGraph())

	if operator.IsNative() {
		return db.buildNativeBinaryExpression(node, operatorMap[node.Kind()])
	}

	leftExpr := db.getExpression(node, parser.NodeBinaryExpressionLeftExpr)
	rightExpr := db.getExpression(node, parser.NodeBinaryExpressionRightExpr)

	leftScope, _ := db.scopegraph.GetScope(node.GetNode(parser.NodeBinaryExpressionLeftExpr))
	parentType := leftScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	optimized, wasOptimized := db.buildOptimizedBinaryOperatorExpression(node, parentType, leftExpr, rightExpr)
	if wasOptimized {
		return optimized
	}

	callExpr := codedom.MemberCall(codedom.StaticMemberReference(operator, parentType, node), operator, []codedom.Expression{leftExpr, rightExpr}, node)
	if modifier != nil {
		return modifier(callExpr)
	}

	return callExpr
}
Example #11
0
// scopeTaggedTemplateString scopes a tagged template string expression in the SRG.
func (sb *scopeBuilder) scopeTaggedTemplateString(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true

	// Scope the tagging expression.
	tagScope := sb.getScope(node.GetNode(parser.NodeTaggedTemplateCallExpression), context)
	if !tagScope.GetIsValid() {
		isValid = false
	}

	// Scope the template string.
	templateScope := sb.getScope(node.GetNode(parser.NodeTaggedTemplateParsed), context)
	if !templateScope.GetIsValid() {
		isValid = false
	}

	// Ensure that the tagging expression is a function of type function<(any here)>(slice<string>, slice<stringable>).
	if tagScope.GetIsValid() {
		tagType := tagScope.ResolvedTypeRef(sb.sg.tdg)
		if !tagType.IsDirectReferenceTo(sb.sg.tdg.FunctionType()) ||
			tagType.ParameterCount() != 2 ||
			tagType.Parameters()[0] != sb.sg.tdg.SliceTypeReference(sb.sg.tdg.StringTypeReference()) ||
			tagType.Parameters()[1] != sb.sg.tdg.SliceTypeReference(sb.sg.tdg.StringableTypeReference()) {

			isValid = false
			sb.decorateWithError(node, "Tagging expression for template string must be function with parameters ([]string, []stringable). Found: %v", tagType)
		}

		return newScope().IsValid(isValid).Resolving(tagType.Generics()[0]).GetScope()
	} else {
		return newScope().IsValid(isValid).Resolving(sb.sg.tdg.AnyTypeReference()).GetScope()
	}
}
Example #12
0
// scopeSliceLiteralExpression scopes a slice literal expression in the SRG.
func (sb *scopeBuilder) scopeSliceLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true

	declaredTypeNode := node.GetNode(parser.NodeSliceLiteralExpressionType)
	declaredType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(declaredTypeNode))
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().Resolving(sb.sg.tdg.SliceTypeReference(sb.sg.tdg.AnyTypeReference())).GetScope()
	}

	// Scope each of the expressions and ensure they match the slice type.
	vit := node.StartQuery().
		Out(parser.NodeSliceLiteralExpressionValue).
		BuildNodeIterator()

	for vit.Next() {
		valueNode := vit.Node()
		valueScope := sb.getScope(valueNode, context)
		if !valueScope.GetIsValid() {
			isValid = false
		} else {
			if serr := valueScope.ResolvedTypeRef(sb.sg.tdg).CheckSubTypeOf(declaredType); serr != nil {
				isValid = false
				sb.decorateWithError(node, "Invalid slice literal value: %v", serr)
			}
		}
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.SliceTypeReference(declaredType)).GetScope()
}
Example #13
0
// scopeConditionalExpression scopes a conditional expression in the SRG.
func (sb *scopeBuilder) scopeConditionalExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	conditionalExprNode := node.GetNode(parser.NodeConditionalExpressionCheckExpression)

	thenContext := sb.inferTypesForConditionalExpressionContext(context, conditionalExprNode, inferredDirect)
	elseContext := sb.inferTypesForConditionalExpressionContext(context, conditionalExprNode, inferredInverted)

	// Scope the child expressions.
	checkScope := sb.getScope(conditionalExprNode, context)
	thenScope := sb.getScope(node.GetNode(parser.NodeConditionalExpressionThenExpression), thenContext)
	elseScope := sb.getScope(node.GetNode(parser.NodeConditionalExpressionElseExpression), elseContext)

	if !checkScope.GetIsValid() || !thenScope.GetIsValid() || !elseScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// Intersect the then and else types.
	thenType := thenScope.ResolvedTypeRef(sb.sg.tdg)
	elseType := elseScope.ResolvedTypeRef(sb.sg.tdg)

	resultType := thenType.Intersect(elseType)

	// Ensure that the check is a boolean expression.
	checkType := checkScope.ResolvedTypeRef(sb.sg.tdg)
	if !checkType.IsDirectReferenceTo(sb.sg.tdg.BoolType()) {
		sb.decorateWithError(node, "Conditional expression check must be of type 'bool', found: %v", checkType)
		return newScope().Invalid().GetScope()
	}

	return newScope().Valid().Resolving(resultType).GetScope()
}
Example #14
0
// scopeBooleanBinaryExpression scopes a boolean binary operator expression in the SRG.
func (sb *scopeBuilder) scopeBooleanBinaryExpression(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 both scopes have type boolean.
	var isValid = true
	leftType := leftScope.ResolvedTypeRef(sb.sg.tdg)
	rightType := rightScope.ResolvedTypeRef(sb.sg.tdg)

	if !leftType.IsDirectReferenceTo(sb.sg.tdg.BoolType()) {
		sb.decorateWithError(node, "Boolean operator requires type Boolean for operands. Left hand operand has type: %v", leftType)
		isValid = false
	}

	if !rightType.IsDirectReferenceTo(sb.sg.tdg.BoolType()) {
		sb.decorateWithError(node, "Boolean operator requires type Boolean for operands. Right hand operand has type: %v", rightType)
		isValid = false
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.BoolTypeReference()).GetScope()
}
Example #15
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 #16
0
// buildRootTypeExpression builds the CodeDOM for a root type expression.
func (db *domBuilder) buildRootTypeExpression(node compilergraph.GraphNode) codedom.Expression {
	childExprNode := node.GetNode(parser.NodeUnaryExpressionChildExpr)
	childScope, _ := db.scopegraph.GetScope(childExprNode)
	childType := childScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	childExpr := db.buildExpression(childExprNode)
	return codedom.NominalUnwrapping(childExpr, childType, node)
}
Example #17
0
// scopeExpressionStatement scopes an expression statement in the SRG.
func (sb *scopeBuilder) scopeExpressionStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	scope := sb.getScope(node.GetNode(parser.NodeExpressionStatementExpression), context)
	if !scope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	return newScope().Valid().GetScope()
}
Example #18
0
// scopeLoopStatement scopes a loop statement in the SRG.
func (sb *scopeBuilder) scopeLoopStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the underlying block.
	blockNode := node.GetNode(parser.NodeLoopStatementBlock)
	blockScope := sb.getScope(blockNode, context.withContinuable(node))

	// If the loop has no expression, it is most likely an infinite loop, so we know it is valid and
	// returns whatever the internal type is.
	loopExprNode, hasExpr := node.TryGetNode(parser.NodeLoopStatementExpression)
	if !hasExpr {
		loopScopeBuilder := newScope().
			IsValid(blockScope.GetIsValid()).
			ReturningTypeOf(blockScope).
			LabelSetOfExcept(blockScope, proto.ScopeLabel_BROKEN_FLOW)

		// If the loop contains a break statement somewhere, then it isn't an infinite
		// loop.
		if !blockScope.HasLabel(proto.ScopeLabel_BROKEN_FLOW) {
			loopScopeBuilder.IsTerminatingStatement()
		}

		// for { ... }
		return loopScopeBuilder.GetScope()
	}

	// Otherwise, scope the expression.
	loopExprScope := sb.getScope(loopExprNode, context)
	if !loopExprScope.GetIsValid() {
		return newScope().
			Invalid().
			GetScope()
	}

	loopExprType := loopExprScope.ResolvedTypeRef(sb.sg.tdg)

	// If the loop has a variable defined, we'll check above that the loop expression is a Stream or Streamable. Otherwise,
	// it must be a boolean value.
	varNode, hasVar := node.TryGetNode(parser.NodeStatementNamedValue)
	if hasVar {
		if !sb.getScope(varNode, context).GetIsValid() {
			return newScope().Invalid().GetScope()
		}

		return newScope().Valid().LabelSetOf(blockScope).GetScope()
	} else {
		if !loopExprType.IsDirectReferenceTo(sb.sg.tdg.BoolType()) {
			sb.decorateWithError(node, "Loop conditional expression must be of type 'bool', found: %v", loopExprType)
			return newScope().
				Invalid().
				GetScope()
		}

		return newScope().Valid().LabelSetOfExcept(blockScope, proto.ScopeLabel_BROKEN_FLOW).GetScope()
	}
}
Example #19
0
// scopeResolveStatement scopes a resolve statement in the SRG.
func (sb *scopeBuilder) scopeResolveStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	destinationScope := sb.getScope(node.GetNode(parser.NodeAssignedDestination), context)
	sourceScope := sb.getScope(node.GetNode(parser.NodeResolveStatementSource), context)

	isValid := sourceScope.GetIsValid() && destinationScope.GetIsValid()

	if rejection, ok := node.TryGetNode(parser.NodeAssignedRejection); ok {
		rejectionScope := sb.getScope(rejection, context)
		isValid = isValid && rejectionScope.GetIsValid()
	}

	return newScope().IsValid(isValid).GetScope()
}
Example #20
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 #21
0
// scopeKeywordNotExpression scopes a keyword not expression in the SRG.
func (sb *scopeBuilder) scopeKeywordNotExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Check for 'is not null' case.
	parentExpr, hasParentExpr := node.TryGetIncomingNode(parser.NodeBinaryExpressionRightExpr)
	if hasParentExpr && parentExpr.Kind() == parser.NodeIsComparisonExpression {
		if node.GetNode(parser.NodeUnaryExpressionChildExpr).Kind() != parser.NodeNullLiteralExpression {
			sb.decorateWithError(node, "Expression under an 'is not' must be 'null'")
			return newScope().Invalid().GetScope()
		}
		return newScope().Valid().GetScope()
	}

	// Check for normal bool-eable.
	return sb.scopeUnaryExpression(node, "bool", parser.NodeUnaryExpressionChildExpr, context).GetScope()
}
Example #22
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 #23
0
// scopeTypeConversionExpression scopes a conversion from a nominal type to a base type.
func (sb *scopeBuilder) scopeTypeConversionExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	childScope := sb.getScope(node.GetNode(parser.NodeFunctionCallExpressionChildExpr), context)
	conversionType := childScope.StaticTypeRef(sb.sg.tdg)

	// Ensure that the function call has a single argument.
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	var index = 0
	var isValid = true

	for ait.Next() {
		if index > 0 {
			sb.decorateWithError(node, "Type conversion requires a single argument")
			isValid = false
			break
		}

		index = index + 1

		// Make sure the argument's scope is valid.
		argumentScope := sb.getScope(ait.Node(), context)
		if !argumentScope.GetIsValid() {
			isValid = false
			continue
		}

		// The argument must be a nominal subtype of the conversion type.
		argumentType := argumentScope.ResolvedTypeRef(sb.sg.tdg)
		if nerr := argumentType.CheckNominalConvertable(conversionType); nerr != nil {
			sb.decorateWithError(node, "Cannot perform type conversion: %v", nerr)
			isValid = false
			break
		}

		if argumentType.IsNullable() {
			conversionType = conversionType.AsNullable()
		}
	}

	if index == 0 {
		sb.decorateWithError(node, "Type conversion requires a single argument")
		isValid = false
	}

	// The type conversion returns an instance of the converted type.
	return newScope().IsValid(isValid).Resolving(conversionType).GetScope()
}
Example #24
0
// scopeConditionalStatement scopes a conditional statement in the SRG.
func (sb *scopeBuilder) scopeConditionalStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var returningType = sb.sg.tdg.VoidTypeReference()
	var valid = true
	var labelSet = newLabelSet()

	conditionalExprNode := node.GetNode(parser.NodeConditionalStatementConditional)

	// Scope the child block(s).
	statementBlockContext := sb.inferTypesForConditionalExpressionContext(context, conditionalExprNode, inferredDirect)
	statementBlockScope := sb.getScope(node.GetNode(parser.NodeConditionalStatementBlock), statementBlockContext)
	labelSet.AppendLabelsOf(statementBlockScope)
	if !statementBlockScope.GetIsValid() {
		valid = false
	}

	var isSettlingScope = false

	elseClauseNode, hasElseClause := node.TryGetNode(parser.NodeConditionalStatementElseClause)
	if hasElseClause {
		elseClauseContext := sb.inferTypesForConditionalExpressionContext(context, conditionalExprNode, inferredInverted)
		elseClauseScope := sb.getScope(elseClauseNode, elseClauseContext)
		labelSet.AppendLabelsOf(elseClauseScope)

		if !elseClauseScope.GetIsValid() {
			valid = false
		}

		// The type returned by this conditional is only non-void if both the block and the
		// else clause return values.
		returningType = statementBlockScope.ReturnedTypeRef(sb.sg.tdg).
			Intersect(elseClauseScope.ReturnedTypeRef(sb.sg.tdg))
		isSettlingScope = statementBlockScope.GetIsSettlingScope() && elseClauseScope.GetIsSettlingScope()
	}

	// Scope the conditional expression and make sure it has type boolean.
	conditionalExprScope := sb.getScope(conditionalExprNode, context)
	if !conditionalExprScope.GetIsValid() {
		return newScope().Invalid().Returning(returningType, isSettlingScope).WithLabelSet(labelSet).GetScope()
	}

	conditionalExprType := conditionalExprScope.ResolvedTypeRef(sb.sg.tdg)
	if !conditionalExprType.IsDirectReferenceTo(sb.sg.tdg.BoolType()) {
		sb.decorateWithError(node, "Conditional expression must be of type 'bool', found: %v", conditionalExprType)
		return newScope().Invalid().Returning(returningType, isSettlingScope).WithLabelSet(labelSet).GetScope()
	}

	return newScope().IsValid(valid).Returning(returningType, isSettlingScope).WithLabelSet(labelSet).GetScope()
}
Example #25
0
// scopeMappingLiteralExpression scopes a mapping literal expression in the SRG.
func (sb *scopeBuilder) scopeMappingLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var isValid = true

	declaredTypeNode := node.GetNode(parser.NodeMappingLiteralExpressionType)
	declaredType, rerr := sb.sg.ResolveSRGTypeRef(sb.sg.srg.GetTypeRef(declaredTypeNode))
	if rerr != nil {
		sb.decorateWithError(node, "%v", rerr)
		return newScope().Invalid().Resolving(sb.sg.tdg.MappingTypeReference(sb.sg.tdg.AnyTypeReference())).GetScope()
	}

	// Scope each of the entries and ensure they match the mapping value type.
	eit := node.StartQuery().
		Out(parser.NodeMappingLiteralExpressionEntryRef).
		BuildNodeIterator()

	for eit.Next() {
		entryNode := eit.Node()

		keyNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryKey)
		valueNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryValue)

		keyScope := sb.getScope(keyNode, context)
		valueScope := sb.getScope(valueNode, context)

		if keyScope.GetIsValid() {
			localKeyType := keyScope.ResolvedTypeRef(sb.sg.tdg)
			if serr := localKeyType.CheckSubTypeOf(sb.sg.tdg.StringableTypeReference()); serr != nil {
				sb.decorateWithError(keyNode, "Mapping literal keys must be of type Stringable: %v", serr)
				isValid = false
			}
		} else {
			isValid = false
		}

		if valueScope.GetIsValid() {
			localValueType := valueScope.ResolvedTypeRef(sb.sg.tdg)
			if serr := localValueType.CheckSubTypeOf(declaredType); serr != nil {
				sb.decorateWithError(keyNode, "Expected mapping values of type %v: %v", declaredType, serr)
				isValid = false
			}
		} else {
			isValid = false
		}
	}

	return newScope().IsValid(isValid).Resolving(sb.sg.tdg.MappingTypeReference(declaredType)).GetScope()
}
Example #26
0
// buildIsComparisonExpression builds the CodeDOM for an is comparison operator.
func (db *domBuilder) buildIsComparisonExpression(node compilergraph.GraphNode) codedom.Expression {
	generatedLeftExpr := db.getExpression(node, parser.NodeBinaryExpressionLeftExpr)

	// Check for a `not` subexpression. If found, we invert the check.
	op := "=="
	rightExpr := node.GetNode(parser.NodeBinaryExpressionRightExpr)
	if rightExpr.Kind() == parser.NodeKeywordNotExpression {
		op = "!="
		rightExpr = rightExpr.GetNode(parser.NodeUnaryExpressionChildExpr)
	}

	generatedRightExpr := db.buildExpression(rightExpr)
	return codedom.NominalWrapping(
		codedom.BinaryOperation(generatedLeftExpr, op, generatedRightExpr, node),
		db.scopegraph.TypeGraph().BoolType(),
		node)
}
Example #27
0
// buildFunctionCall builds the CodeDOM for a function call.
func (db *domBuilder) buildFunctionCall(node compilergraph.GraphNode) codedom.Expression {
	childExprNode := node.GetNode(parser.NodeFunctionCallExpressionChildExpr)
	childScope, _ := db.scopegraph.GetScope(childExprNode)

	// Check if the child expression has a static scope. If so, this is a type conversion between
	// a nominal type and a base type.
	if childScope.GetKind() == proto.ScopeKind_STATIC {
		wrappedExprNode := node.GetNode(parser.NodeFunctionCallArgument)
		wrappedExprScope, _ := db.scopegraph.GetScope(wrappedExprNode)
		wrappedExprType := wrappedExprScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

		wrappedExpr := db.buildExpression(wrappedExprNode)

		targetTypeRef := childScope.StaticTypeRef(db.scopegraph.TypeGraph())

		// If the targetTypeRef is not nominal or structural, then we know we are unwrapping.
		if !targetTypeRef.IsNominalOrStruct() {
			return codedom.NominalUnwrapping(wrappedExpr, wrappedExprType, node)
		} else {
			return codedom.NominalRefWrapping(wrappedExpr, wrappedExprType, targetTypeRef, node)
		}
	}

	// Collect the expressions for the arguments.
	ait := node.StartQuery().
		Out(parser.NodeFunctionCallArgument).
		BuildNodeIterator()

	arguments := db.buildExpressions(ait, buildExprCheckNominalShortcutting)
	childExpr := db.buildExpression(childExprNode)

	// If the function call is to a member, then we return a MemberCall.
	namedRef, isNamed := db.scopegraph.GetReferencedName(childScope)
	if isNamed && !namedRef.IsLocal() {
		member, _ := namedRef.Member()

		if childExprNode.Kind() == parser.NodeNullableMemberAccessExpression {
			return codedom.NullableMemberCall(childExpr, member, arguments, node)
		}

		return codedom.MemberCall(childExpr, member, arguments, node)
	}

	// Otherwise, this is a normal function call with an await.
	return codedom.AwaitPromise(codedom.FunctionCall(childExpr, arguments, node), node)
}
Example #28
0
// scopeAssertNotNullExpression scopes an assert-not-null operator expression in the SRG.
func (sb *scopeBuilder) scopeAssertNotNullExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Get the scope of the child expression.
	childScope := sb.getScope(node.GetNode(parser.NodeUnaryExpressionChildExpr), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	nullableType := childScope.ResolvedTypeRef(sb.sg.tdg)

	// Ensure that the nullable type is nullable.
	if !nullableType.IsNullable() {
		sb.decorateWithError(node, "Child expression of an assert not nullable operator must be nullable. Found: %v", nullableType)
		return newScope().Invalid().GetScope()
	}

	return newScope().Valid().Resolving(nullableType.AsNonNullable()).GetScope()
}
Example #29
0
// buildLoopExpression builds the CodeDOM for a loop expression.
func (db *domBuilder) buildLoopExpression(node compilergraph.GraphNode) codedom.Expression {
	member, found := db.scopegraph.TypeGraph().StreamType().ParentModule().FindMember("MapStream")
	if !found {
		panic("Missing MapStream function under Stream's module")
	}

	// Retrieve the item type for the members of the stream and the mapped values.
	mapExpr := node.GetNode(parser.NodeLoopExpressionMapExpression)
	namedValue := node.GetNode(parser.NodeLoopExpressionNamedValue)

	namedScope, _ := db.scopegraph.GetScope(namedValue)
	namedItemType := namedScope.AssignableTypeRef(db.scopegraph.TypeGraph())

	mapScope, _ := db.scopegraph.GetScope(mapExpr)
	mappedItemType := mapScope.ResolvedTypeRef(db.scopegraph.TypeGraph())

	// Build a reference to the Map function.
	mapFunctionReference := codedom.FunctionCall(
		codedom.StaticMemberReference(member, db.scopegraph.TypeGraph().StreamTypeReference(mappedItemType), node),
		[]codedom.Expression{
			codedom.TypeLiteral(namedItemType, node),
			codedom.TypeLiteral(mappedItemType, node),
		},
		node)

	// A loop expression is replaced with a call to the Map function, with the stream as the first parameter
	// and a mapper function which resolves the mapped value as the second.
	builtMapExpr := db.buildExpression(mapExpr)
	builtStreamExpr := db.getExpression(node, parser.NodeLoopExpressionStreamExpression)

	loopValueName := namedValue.Get(parser.NodeNamedValueName)
	mapperFunction := codedom.FunctionDefinition(
		[]string{},
		[]string{loopValueName},
		codedom.Resolution(builtMapExpr, builtMapExpr.BasisNode()),
		false,
		codedom.NormalFunction,
		builtMapExpr.BasisNode())

	return codedom.AwaitPromise(
		codedom.FunctionCall(mapFunctionReference,
			[]codedom.Expression{builtStreamExpr, mapperFunction},
			node),
		node)
}
Example #30
0
// scopeStructuralNewExpression scopes a structural new-type expressions.
func (sb *scopeBuilder) scopeStructuralNewExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	// Scope the child expression and ensure it refers to a type or an existing struct.
	childScope := sb.getScope(node.GetNode(parser.NodeStructuralNewTypeExpression), context)
	if !childScope.GetIsValid() {
		return newScope().Invalid().GetScope()
	}

	// If the child scope refers to a static type, then we are constructing a new instance of that
	// type. Otherwise, we are "modifying" an existing structural instance via a clone.
	if childScope.GetKind() == proto.ScopeKind_STATIC {
		return sb.scopeStructuralNewTypeExpression(node, childScope, context)
	} else if childScope.GetKind() == proto.ScopeKind_VALUE {
		return sb.scopeStructuralNewCloneExpression(node, childScope, context)
	} else {
		sb.decorateWithError(node, "Cannot construct non-type, non-struct expression")
		return newScope().Invalid().GetScope()
	}
}