// 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 }
// scopeLambdaExpression scopes a lambda expression in the SRG. func (sb *scopeBuilder) scopeLambdaExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { if _, ok := node.TryGetNode(parser.NodeLambdaExpressionBlock); ok { return sb.scopeFullLambaExpression(node, context) } else { return sb.scopeInlineLambaExpression(node, context) } }
// AsImplementable returns the given node as an SRGImplementable (if applicable). func (g *SRG) AsImplementable(node compilergraph.GraphNode) (SRGImplementable, bool) { switch node.Kind() { case parser.NodeTypeConstructor: fallthrough case parser.NodeTypeFunction: fallthrough case parser.NodeTypeProperty: fallthrough case parser.NodeTypeOperator: fallthrough case parser.NodeTypeField: fallthrough case parser.NodeTypeVariable: fallthrough case parser.NodeTypePropertyBlock: return SRGImplementable{node, g}, true default: return SRGImplementable{}, false } }
// 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() }
// 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() }
// buildMappingLiteralExpression builds the CodeDOM for a mapping literal expression. func (db *domBuilder) buildMappingLiteralExpression(node compilergraph.GraphNode) codedom.Expression { mappingScope, _ := db.scopegraph.GetScope(node) mappingType := mappingScope.ResolvedTypeRef(db.scopegraph.TypeGraph()) eit := node.StartQuery(). Out(parser.NodeMappingLiteralExpressionEntryRef). BuildNodeIterator() var entries = make([]codedom.ObjectLiteralEntryNode, 0) for eit.Next() { entryNode := eit.Node() // The key expression must be a string when produced. We either reference it directly (if a string) // or call .String() (if a Stringable). keyNode := entryNode.GetNode(parser.NodeMappingLiteralExpressionEntryKey) keyScope, _ := db.scopegraph.GetScope(keyNode) keyType := keyScope.ResolvedTypeRef(db.scopegraph.TypeGraph()) var keyExpr = db.buildExpression(keyNode) if !keyType.HasReferredType(db.scopegraph.TypeGraph().StringType()) { stringMethod, _ := keyType.ResolveMember("String", typegraph.MemberResolutionInstance) keyExpr = codedom.MemberCall( codedom.MemberReference(db.buildExpression(keyNode), stringMethod, node), stringMethod, []codedom.Expression{}, keyNode) } // Get the expression for the value. valueExpr := db.getExpression(entryNode, parser.NodeMappingLiteralExpressionEntryValue) // Build an object literal expression with the (native version of the) key string and the // created value. entryExpr := codedom.ObjectLiteralEntryNode{ codedom.NominalUnwrapping(keyExpr, db.scopegraph.TypeGraph().StringTypeReference(), keyNode), valueExpr, entryNode, } entries = append(entries, entryExpr) } if len(entries) == 0 { // Empty mapping. Call the Empty() constructor directly. constructor, _ := mappingType.ResolveMember("Empty", typegraph.MemberResolutionStatic) return codedom.MemberCall( codedom.MemberReference(codedom.TypeLiteral(mappingType, node), constructor, node), constructor, []codedom.Expression{}, node) } constructor, _ := mappingType.ResolveMember("overObject", typegraph.MemberResolutionStatic) return codedom.MemberCall( codedom.MemberReference(codedom.TypeLiteral(mappingType, node), constructor, node), constructor, []codedom.Expression{codedom.ObjectLiteral(entries, node)}, node) }
// scopeListLiteralExpression scopes a list literal expression in the SRG. func (sb *scopeBuilder) scopeListLiteralExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { var isValid = true var valueType = sb.sg.tdg.VoidTypeReference() // Scope each of the expressions and determine the list type based on its contents. vit := node.StartQuery(). Out(parser.NodeListExpressionValue). BuildNodeIterator() for vit.Next() { valueNode := vit.Node() valueScope := sb.getScope(valueNode, context) if !valueScope.GetIsValid() { isValid = false } else { valueType = valueType.Intersect(valueScope.ResolvedTypeRef(sb.sg.tdg)) } } if valueType.IsVoid() { valueType = sb.sg.tdg.AnyTypeReference() } return newScope().IsValid(isValid).Resolving(sb.sg.tdg.ListTypeReference(valueType)).GetScope() }
// 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) } }
// 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)) }
// 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() }
func (db *domBuilder) buildLambdaExpressionInternal(node compilergraph.GraphNode, paramPredicate compilergraph.Predicate, body codedom.StatementOrExpression, isGenerator bool) codedom.Expression { // Collect the generic names and parameter names of the lambda expression. var generics = make([]string, 0) var parameters = make([]string, 0) git := node.StartQuery(). Out(parser.NodePredicateTypeMemberGeneric). BuildNodeIterator(parser.NodeGenericPredicateName) for git.Next() { generics = append(generics, git.GetPredicate(parser.NodeGenericPredicateName).String()) } pit := node.StartQuery(). Out(paramPredicate). BuildNodeIterator(parser.NodeLambdaExpressionParameterName) for pit.Next() { parameters = append(parameters, pit.GetPredicate(parser.NodeLambdaExpressionParameterName).String()) } // Check for a generator. specialization := codedom.NormalFunction if isGenerator { specialization = codedom.GeneratorFunction } return codedom.FunctionDefinition(generics, parameters, body, false, specialization, node) }
// 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() }
// 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() }
// 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() }
// 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 } }
// 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() } }
// 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 }
// buildMatchStatement builds the CodeDOM for a match statement. func (db *domBuilder) buildMatchStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) { // Retrieve (or generate) the name of a variable to hold the value being matched against. var matchExprVarName = "" if namedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue); hasNamedValue { matchExprVarName = namedValue.Get(parser.NodeNamedValueName) } else { matchExprVarName = db.generateScopeVarName(node) } // Set the match expression's value into the variable. startStatement := codedom.VarDefinitionWithInit(matchExprVarName, db.getExpression(node, parser.NodeMatchStatementExpression), node) getCheckExpression := func(caseTypeRefNode compilergraph.GraphNode) codedom.Expression { caseTypeLiteral, _ := db.scopegraph.ResolveSRGTypeRef( db.scopegraph.SourceGraph().GetTypeRef(caseTypeRefNode)) return codedom.NominalWrapping( codedom.RuntimeFunctionCall(codedom.IsTypeFunction, []codedom.Expression{ codedom.LocalReference(matchExprVarName, caseTypeRefNode), codedom.TypeLiteral(caseTypeLiteral, caseTypeRefNode), }, caseTypeRefNode), db.scopegraph.TypeGraph().BoolType(), caseTypeRefNode) } return db.buildJumpingCaseStatement(node, parser.NodeMatchStatementCase, parser.NodeMatchStatementCaseStatement, parser.NodeMatchStatementCaseTypeReference, startStatement, getCheckExpression) }
// 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) }
// buildMapExpression builds the CodeDOM for a map expression. func (db *domBuilder) buildMapExpression(node compilergraph.GraphNode) codedom.Expression { mapScope, _ := db.scopegraph.GetScope(node) mapType := mapScope.ResolvedTypeRef(db.scopegraph.TypeGraph()) eit := node.StartQuery(). Out(parser.NodeMapExpressionChildEntry). BuildNodeIterator() var keyExprs = make([]codedom.Expression, 0) var valueExprs = make([]codedom.Expression, 0) for eit.Next() { entryNode := eit.Node() keyExprs = append(keyExprs, db.getExpression(entryNode, parser.NodeMapExpressionEntryKey)) valueExprs = append(valueExprs, db.getExpression(entryNode, parser.NodeMapExpressionEntryValue)) } if len(valueExprs) == 0 { // Empty map. Call the new() constructor directly. constructor, _ := mapType.ResolveMember("new", typegraph.MemberResolutionStatic) return codedom.MemberCall( codedom.MemberReference(codedom.TypeLiteral(mapType, node), constructor, node), constructor, []codedom.Expression{}, node) } constructor, _ := mapType.ResolveMember("forArrays", typegraph.MemberResolutionStatic) return codedom.MemberCall( codedom.MemberReference(codedom.TypeLiteral(mapType, node), constructor, node), constructor, []codedom.Expression{codedom.ArrayLiteral(keyExprs, node), codedom.ArrayLiteral(valueExprs, node)}, node) }
// 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() } }
// 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) }
// scopeImplementedMember scopes an implemented type member. func (sb *scopeBuilder) scopeImplementedMember(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { if body, hasBody := node.TryGetNode(parser.NodePredicateBody); hasBody { scope := sb.getScope(body, context) return *scope } else { return newScope().GetScope() } }
// tryGetExpression attempts to retrieve the predicate on the given node and, if found, build it as an // expression. func (db *domBuilder) tryGetExpression(node compilergraph.GraphNode, predicate compilergraph.Predicate) (codedom.Expression, bool) { childNode, hasChild := node.TryGetNode(predicate) if !hasChild { return nil, false } return db.buildExpression(childNode), true }
// 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() }
// tryGetStatements attempts to retrieve the predicate on the given node and, if found, build it as // start and end statements. func (db *domBuilder) tryGetStatements(node compilergraph.GraphNode, predicate compilergraph.Predicate) (codedom.Statement, codedom.Statement, bool) { childNode, hasChild := node.TryGetNode(predicate) if !hasChild { return nil, nil, false } start, end := db.buildStatements(childNode) return start, end, true }
// buildSliceExpression builds the CodeDOM for a slicer or indexer expression. func (db *domBuilder) buildSliceExpression(node compilergraph.GraphNode) codedom.Expression { // Check if this is a slice vs an index. _, isIndexer := node.TryGetNode(parser.NodeSliceExpressionIndex) if isIndexer { return db.buildIndexerExpression(node) } else { return db.buildSlicerExpression(node) } }
// 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, ) }
// scopeSliceExpression scopes a slice expression in the SRG. func (sb *scopeBuilder) scopeSliceExpression(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo { // Check if this is a slice vs an index. _, isIndexer := node.TryGetNode(parser.NodeSliceExpressionIndex) if isIndexer { return sb.scopeIndexerExpression(node, context) } else { return sb.scopeSlicerExpression(node, context) } }
// 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) }