예제 #1
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
}
예제 #2
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)
}
예제 #3
0
// 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)
}
예제 #4
0
// generateNominalWrapping generates the expression source for the nominal wrapping of an instance of a base type.
func (eg *expressionGenerator) generateNominalWrapping(nominalWrapping *codedom.NominalWrappingNode, context generationContext) esbuilder.ExpressionBuilder {
	// If this is a wrap is of an unwrap, then cancel both operations if we are simply rewrapping
	// the unwrapped type.
	if unwrapping, ok := nominalWrapping.ChildExpression.(*codedom.NominalUnwrappingNode); ok {
		if unwrapping.ChildExpressionType.NominalDataType() == nominalWrapping.NominalTypeRef {
			// Skip entirely.
			return eg.generateExpression(unwrapping.ChildExpression, context)
		}
	}

	// If the child expression being wrapped is non-nullable and not a nominal or struct,
	// then we can use a fast-path box call without the extra unboxing.
	boxFunction := codedom.BoxFunction
	if !nominalWrapping.ChildExpressionType.NullValueAllowed() &&
		(!nominalWrapping.ChildExpressionType.IsNominalOrStruct() || nominalWrapping.IsLiteralWrap) {
		boxFunction = codedom.FastBoxFunction
	}

	call := codedom.RuntimeFunctionCall(
		boxFunction,
		[]codedom.Expression{
			nominalWrapping.ChildExpression,
			codedom.TypeLiteral(nominalWrapping.NominalTypeRef, nominalWrapping.BasisNode())},
		nominalWrapping.BasisNode())
	return eg.generateExpression(call, context)
}
예제 #5
0
// generateNominalUnwrapping generates the expression source for the unwrapping of a nominal instance of a base type.
func (eg *expressionGenerator) generateNominalUnwrapping(nominalUnwrapping *codedom.NominalUnwrappingNode, context generationContext) esbuilder.ExpressionBuilder {
	// If this is an unwrap is of a wrap, then try to either cancel both operations (if we can),
	// or simplify the unwrapping operation.
	childExpression := nominalUnwrapping.ChildExpression
	if wrapping, ok := nominalUnwrapping.ChildExpression.(*codedom.NominalWrappingNode); ok {
		// If the value that was being wrapped is, itself, nominal or structural, then we still
		// need to unwrap it, but we can fast-path by ignoring the wrap and directly unwrapping
		// the nominal/struct. Otherwise, we can simply just collapse the unwrap+wrap into a NOOP.
		if !wrapping.IsLiteralWrap && wrapping.ChildExpressionType.IsNominalOrStruct() {
			childExpression = wrapping.ChildExpression
		} else {
			// Otherwise, we can just collapse both operations into nothing.
			return eg.generateExpression(wrapping.ChildExpression, context)
		}
	}

	// If the child expression being unwrapped is non-nullable and we know it is boxed, then
	// we can use a fast-path unbox call without the extra checks.
	if !nominalUnwrapping.ChildExpressionType.NullValueAllowed() &&
		nominalUnwrapping.ChildExpressionType.IsNominalOrStruct() {
		access := codedom.NativeAccess(
			childExpression,
			codedom.BoxedDataProperty,
			nominalUnwrapping.BasisNode())
		return eg.generateExpression(access, context)
	}

	call := codedom.RuntimeFunctionCall(
		codedom.UnboxFunction,
		[]codedom.Expression{
			childExpression,
		},
		nominalUnwrapping.BasisNode())
	return eg.generateExpression(call, context)
}
예제 #6
0
// buildAwaitExpression builds the CodeDOM for an await expression.
func (db *domBuilder) buildAwaitExpression(node compilergraph.GraphNode) codedom.Expression {
	sourceExpr := codedom.RuntimeFunctionCall(
		codedom.TranslatePromiseFunction,
		[]codedom.Expression{db.getExpression(node, parser.NodeAwaitExpressionSource)},
		node)

	return codedom.AwaitPromise(sourceExpr, node)
}
예제 #7
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,
	)
}
예제 #8
0
// generateWithShortCircuiting generates an expression with automatic short circuiting based on the value found
// in the resultName variable and compared to the given compare value.
func (eg *expressionGenerator) generateWithShortCircuiting(expr codedom.Expression, resultName string, compareValue codedom.Expression, context generationContext) esbuilder.ExpressionBuilder {
	shortCircuiter := eg.generateExpression(
		codedom.RuntimeFunctionCall(codedom.ShortCircuitPromiseFunction,
			[]codedom.Expression{codedom.LocalReference(resultName, expr.BasisNode()), compareValue},
			expr.BasisNode()),
		context)

	return eg.generateExpression(expr, generationContext{shortCircuiter})
}
예제 #9
0
// generateDynamicAccess generates the expression source for dynamic access.
func (eg *expressionGenerator) generateDynamicAccess(dynamicAccess *codedom.DynamicAccessNode, context generationContext) esbuilder.ExpressionBuilder {
	basisNode := dynamicAccess.BasisNode()
	funcCall := codedom.RuntimeFunctionCall(
		codedom.DynamicAccessFunction,
		[]codedom.Expression{
			dynamicAccess.ChildExpression,
			codedom.LiteralValue("'"+dynamicAccess.Name+"'", basisNode),
		},
		basisNode,
	)

	// All dynamic accesses return a promise, to ensure it works for properties.
	return eg.generateExpression(codedom.AwaitPromise(funcCall, basisNode), context)
}
예제 #10
0
// popResource removes a resource from the resource stack.
func (sg *stateGenerator) popResource(name string, basis compilergraph.GraphNode) {
	sg.managesResources = true
	sg.resources.Pop()

	// popr('varName').then(...)
	popCall := codedom.RuntimeFunctionCall(
		codedom.StatePopResourceFunction,
		[]codedom.Expression{
			codedom.LiteralValue("'"+name+"'", basis),
		},
		basis,
	)

	sg.generateStates(codedom.ExpressionStatement(codedom.AwaitPromise(popCall, basis), basis),
		generateImplicitState)
}
예제 #11
0
func (eg *expressionGenerator) generateShortCircuiter(compareExpr codedom.Expression,
	compareValue codedom.Expression,
	childExpr codedom.Expression,
	context generationContext,
	handler shortCircuitHandler) esbuilder.ExpressionBuilder {

	// Generate a specialized wrapper which resolves the left side value and places it into the result.
	resultName := eg.generateUniqueName("$result")
	resolveCompareValue := codedom.RuntimeFunctionCall(codedom.ResolvePromiseFunction,
		[]codedom.Expression{compareExpr},
		compareExpr.BasisNode())

	eg.addAsyncWrapper(eg.generateExpression(resolveCompareValue, context), resultName)

	shortedChildExpr := eg.generateWithShortCircuiting(childExpr, resultName, compareValue, context)
	return handler(resultName, shortedChildExpr)
}
예제 #12
0
// generateTernary generates the expression for a ternary expression.
func (eg *expressionGenerator) generateTernary(ternary *codedom.TernaryNode, context generationContext) esbuilder.ExpressionBuilder {
	// Generate a specialized wrapper which resolves the conditional value of the ternary and
	// places it into the result.
	resultName := eg.generateUniqueName("$result")
	resolveConditionalValue := codedom.RuntimeFunctionCall(codedom.ResolvePromiseFunction,
		[]codedom.Expression{codedom.NominalUnwrapping(ternary.CheckExpr, eg.scopegraph.TypeGraph().BoolTypeReference(), ternary.CheckExpr.BasisNode())},
		ternary.BasisNode())

	eg.addAsyncWrapper(eg.generateExpression(resolveConditionalValue, context), resultName)

	// Generate the then and else expressions as short circuited by the value.
	thenExpr := eg.generateWithShortCircuiting(ternary.ThenExpr, resultName, codedom.LiteralValue("true", ternary.BasisNode()), context)
	elseExpr := eg.generateWithShortCircuiting(ternary.ElseExpr, resultName, codedom.LiteralValue("false", ternary.BasisNode()), context)

	// Return an expression which compares the value and either return the then or else values.
	return esbuilder.Ternary(esbuilder.Identifier(resultName), thenExpr, elseExpr)
}
예제 #13
0
// pushResource adds a resource to the resource stack with the given name.
func (sg *stateGenerator) pushResource(name string, basis compilergraph.GraphNode) {
	sg.managesResources = true

	// pushr(varName, 'varName');
	pushCall := codedom.RuntimeFunctionCall(
		codedom.StatePushResourceFunction,
		[]codedom.Expression{
			codedom.LocalReference(name, basis),
			codedom.LiteralValue("'"+name+"'", basis),
		},
		basis,
	)

	sg.generateStates(codedom.ExpressionStatement(pushCall, basis), generateImplicitState)
	sg.resources.Push(resource{
		name:  name,
		basis: basis,
	})
}
예제 #14
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)
}
예제 #15
0
// buildAssertNotNullExpression builds the CodeDOM for an assert not null (expr!) operator.
func (db *domBuilder) buildAssertNotNullExpression(node compilergraph.GraphNode) codedom.Expression {
	childExpr := db.getExpression(node, parser.NodeUnaryExpressionChildExpr)
	return codedom.RuntimeFunctionCall(codedom.AssertNotNullFunction, []codedom.Expression{childExpr}, node)
}
예제 #16
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
}