Example #1
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 #2
0
// buildTemplateStringCall builds the CodeDOM representing the call to a template string function.
func (db *domBuilder) buildTemplateStringCall(node compilergraph.GraphNode, funcExpr codedom.Expression, isTagged bool) codedom.Expression {
	pit := node.StartQuery().
		Out(parser.NodeTemplateStringPiece).
		BuildNodeIterator()

	var pieceExprs = make([]codedom.Expression, 0)
	var valueExprs = make([]codedom.Expression, 0)

	var isPiece = true
	for pit.Next() {
		if isPiece {
			pieceExprs = append(pieceExprs, db.buildExpression(pit.Node()))
		} else {
			valueExprs = append(valueExprs, db.buildExpression(pit.Node()))
		}

		isPiece = !isPiece
	}

	// Handle common case: No literal string piece at all.
	if len(pieceExprs) == 0 {
		return codedom.NominalWrapping(codedom.LiteralValue("''", node), db.scopegraph.TypeGraph().StringType(), node)
	}

	// Handle common case: A single literal string piece with no values.
	if len(pieceExprs) == 1 && len(valueExprs) == 0 {
		return pieceExprs[0]
	}

	pieceSliceType := db.scopegraph.TypeGraph().SliceTypeReference(db.scopegraph.TypeGraph().StringTypeReference())
	valueSliceType := db.scopegraph.TypeGraph().SliceTypeReference(db.scopegraph.TypeGraph().StringableTypeReference())

	constructor, _ := pieceSliceType.ResolveMember("overArray", typegraph.MemberResolutionStatic)

	pieceSliceExpr := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(pieceSliceType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ArrayLiteral(pieceExprs, node)},
		node)

	valueSliceExpr := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(valueSliceType, node), constructor, node),
		constructor,
		[]codedom.Expression{codedom.ArrayLiteral(valueExprs, node)},
		node)

	return codedom.AwaitPromise(codedom.FunctionCall(funcExpr, []codedom.Expression{pieceSliceExpr, valueSliceExpr}, node), node)
}
Example #3
0
// buildGenericSpecifierExpression builds the CodeDOM for a generic specification of a function or type.
func (db *domBuilder) buildGenericSpecifierExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeGenericSpecifierChildExpr)

	// Collect the generic types being specified.
	git := node.StartQuery().
		Out(parser.NodeGenericSpecifierType).
		BuildNodeIterator()

	var genericTypes = make([]codedom.Expression, 0)
	for git.Next() {
		replacementType, _ := db.scopegraph.ResolveSRGTypeRef(db.scopegraph.SourceGraph().GetTypeRef(git.Node()))
		genericTypes = append(genericTypes, codedom.TypeLiteral(replacementType, git.Node()))
	}

	return codedom.FunctionCall(childExpr, genericTypes, node)
}
Example #4
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 #5
0
// generateMemberCall generates the expression source for a call to a module or type member.
func (eg *expressionGenerator) generateMemberCall(memberCall *codedom.MemberCallNode, context generationContext) esbuilder.ExpressionBuilder {
	if memberCall.Member.IsOperator() && memberCall.Member.IsNative() {
		// This is a call to a native operator.
		if memberCall.Member.Name() != "index" {
			panic("Native call to non-index operator")
		}

		refExpr := memberCall.ChildExpression.(*codedom.MemberReferenceNode).ChildExpression
		return eg.generateExpression(codedom.NativeIndexing(refExpr, memberCall.Arguments[0], memberCall.BasisNode()), context)
	}

	callPath := memberCall.ChildExpression
	arguments := memberCall.Arguments

	var functionCall = codedom.FunctionCall(callPath, arguments, memberCall.BasisNode())
	if memberCall.Nullable {
		// Invoke the function with a specialized nullable-invoke.
		refExpr := callPath.(*codedom.DynamicAccessNode).ChildExpression

		var isPromising = "false"
		if memberCall.Member.IsPromising() {
			isPromising = "true"
		}

		localArguments := []codedom.Expression{
			refExpr,
			codedom.LiteralValue("'"+memberCall.Member.Name()+"'", refExpr.BasisNode()),
			codedom.LiteralValue(isPromising, memberCall.BasisNode()),
			codedom.ArrayLiteral(arguments, memberCall.BasisNode()),
		}

		functionCall = codedom.RuntimeFunctionCall(codedom.NullableInvokeFunction, localArguments, memberCall.BasisNode())
	}

	return eg.generateExpression(codedom.WrapIfPromising(functionCall, memberCall.Member, memberCall.BasisNode()), context)
}
Example #6
0
// buildSmlExpression builds the CodeDOM for a SML expression.
func (db *domBuilder) buildSmlExpression(node compilergraph.GraphNode) codedom.Expression {
	smlScope, _ := db.scopegraph.GetScope(node)

	funcOrTypeRefNode := node.GetNode(parser.NodeSmlExpressionTypeOrFunction)
	funcOrTypeRefScope, _ := db.scopegraph.GetScope(funcOrTypeRefNode)

	// Build the expression for the function or constructor to be called to construct the expression.
	var declarationFunction = db.buildExpression(funcOrTypeRefNode)
	var declarationFunctionType = db.scopegraph.TypeGraph().AnyTypeReference()

	if smlScope.HasLabel(proto.ScopeLabel_SML_CONSTRUCTOR) {
		constructor, _ := funcOrTypeRefScope.StaticTypeRef(db.scopegraph.TypeGraph()).ResolveMember("Declare", typegraph.MemberResolutionStatic)
		declarationFunctionType = constructor.MemberType()
		declarationFunction = codedom.MemberReference(declarationFunction, constructor, node)
	} else {
		declarationFunctionType = funcOrTypeRefScope.ResolvedTypeRef(db.scopegraph.TypeGraph())
	}

	var declarationArguments = make([]codedom.Expression, 0)
	declFunctionParams := declarationFunctionType.Parameters()

	// If the SML expression expects any attributes, then construct the props struct, class or mapping.
	if len(declFunctionParams) > 0 {
		attributeExpressions := map[string]codedom.Expression{}

		ait := node.StartQuery().
			Out(parser.NodeSmlExpressionAttribute).
			BuildNodeIterator()

		for ait.Next() {
			attributeNode := ait.Node()
			attributeName := attributeNode.Get(parser.NodeSmlAttributeName)
			attributeExpressions[attributeName] = db.getExpressionOrDefault(
				attributeNode,
				parser.NodeSmlAttributeValue,
				codedom.NominalWrapping(
					codedom.LiteralValue("true", attributeNode),
					db.scopegraph.TypeGraph().BoolType(),
					attributeNode))
		}

		// Construct the props object expression, either as a struct, class or as a mapping.
		propsType := declFunctionParams[0]
		if smlScope.HasLabel(proto.ScopeLabel_SML_PROPS_MAPPING) {
			declarationArguments = append(declarationArguments, db.buildMappingInitializerExpression(propsType, attributeExpressions, node))
		} else {
			declarationArguments = append(declarationArguments, db.buildStructInitializerExpression(propsType, attributeExpressions, node))
		}
	}

	// If the SML expression expects any children, then construct the children stream or value (if any).
	if len(declFunctionParams) > 1 {
		cit := node.StartQuery().
			Out(parser.NodeSmlExpressionChild).
			BuildNodeIterator()

		children := db.buildExpressions(cit, buildExprNormally)

		switch {
		case smlScope.HasLabel(proto.ScopeLabel_SML_SINGLE_CHILD):
			// If there is a child, it is a value. If missing, the child is nullable, so we don't put
			// anything there.
			if len(children) > 0 {
				declarationArguments = append(declarationArguments, children[0])
			}

		case smlScope.HasLabel(proto.ScopeLabel_SML_STREAM_CHILD):
			// Single child which is a stream. Passed directly to the declaring function as an argument.
			declarationArguments = append(declarationArguments, children[0])

		case smlScope.HasLabel(proto.ScopeLabel_SML_CHILDREN):
			// The argument is a stream of the child expressions. Build it as a generator.
			if len(children) == 0 {
				// Add an empty generator.
				declarationArguments = append(declarationArguments,
					codedom.RuntimeFunctionCall(codedom.EmptyGeneratorDirect, []codedom.Expression{}, node))
			} else {
				yielders := make([]codedom.Statement, len(children))
				for index, child := range children {
					yielders[index] = codedom.YieldValue(child, child.BasisNode())
					if index > 0 {
						yielders[index-1].(codedom.HasNextStatement).SetNext(yielders[index])
					}
				}

				generatorFunc := codedom.FunctionDefinition([]string{}, []string{}, yielders[0], false, codedom.GeneratorFunction, node)
				generatorExpr := codedom.FunctionCall(generatorFunc, []codedom.Expression{}, node)
				declarationArguments = append(declarationArguments, codedom.AwaitPromise(generatorExpr, node))
			}
		default:
			panic("Unknown SML children scope label")
		}
	}

	// The value of declaration function invoked with the arguments is the initial definition.
	var definition = codedom.AwaitPromise(
		codedom.FunctionCall(declarationFunction, declarationArguments, node), node)

	// Add any decorators as wrapping around the definition call.
	dit := node.StartQuery().
		Out(parser.NodeSmlExpressionDecorator).
		BuildNodeIterator()

	for dit.Next() {
		decoratorNode := dit.Node()
		decoratorFunc := db.getExpression(decoratorNode, parser.NodeSmlDecoratorPath)

		// The value for the decorator is either the value expression given or the literal
		// value "true" if none specified.
		decoratorValue := db.getExpressionOrDefault(
			decoratorNode,
			parser.NodeSmlDecoratorValue,
			codedom.NominalWrapping(
				codedom.LiteralValue("true", decoratorNode),
				db.scopegraph.TypeGraph().BoolType(),
				decoratorNode))

		// The decorator is invoked over the definition.
		decoratorArgs := []codedom.Expression{definition, decoratorValue}
		definition = codedom.AwaitPromise(
			codedom.FunctionCall(decoratorFunc, decoratorArgs, decoratorNode),
			decoratorNode)
	}

	return definition
}