Ejemplo n.º 1
0
// buildInitializationCompoundExpression builds the compound expression for calling the specified initializers on a struct.
func (db *domBuilder) buildInitializationCompoundExpression(structType typegraph.TypeReference, initializers map[string]codedom.Expression, structValue codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	// Create a variable to hold the struct instance.
	structInstanceVarName := db.generateScopeVarName(node)

	// Build the assignment expressions.
	assignmentExpressions := make([]codedom.Expression, len(initializers))
	var index = 0
	for fieldName, initializer := range initializers {
		member, found := structType.ResolveMember(fieldName, typegraph.MemberResolutionInstance)
		if !found {
			panic("Member not found in struct initializer construction")
		}

		assignmentExpressions[index] =
			codedom.MemberAssignment(member,
				codedom.MemberReference(
					codedom.LocalReference(structInstanceVarName, node),
					member,
					node),
				initializer,
				node)

		index = index + 1
	}

	// Return a compound expression that takes in the struct value and the assignment expressions,
	// executes them, and returns the struct value.
	return codedom.CompoundExpression(structInstanceVarName, structValue, assignmentExpressions, codedom.LocalReference(structInstanceVarName, node), node)
}
Ejemplo n.º 2
0
// buildStructInitializerExpression builds an initializer expression for a struct type.
func (db *domBuilder) buildStructInitializerExpression(structType typegraph.TypeReference, initializers map[string]codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	staticType := structType.ReferredType()

	var arguments = make([]codedom.Expression, 0)
	for _, field := range staticType.RequiredFields() {
		arguments = append(arguments, initializers[field.Name()])
		delete(initializers, field.Name())
	}

	constructor, found := structType.ResolveMember("new", typegraph.MemberResolutionStatic)
	if !found {
		panic(fmt.Sprintf("Missing new constructor on type %v", structType))
	}

	newCall := codedom.MemberCall(
		codedom.MemberReference(
			codedom.TypeLiteral(structType, node),
			constructor,
			node),
		constructor,
		arguments,
		node)

	// If there are no initializers, then just return the new value directly.
	if len(initializers) == 0 {
		return newCall
	}

	return db.buildInitializationCompoundExpression(structType, initializers, newCall, node)
}
Ejemplo n.º 3
0
// buildMappingInitializerExpression builds the CodeDOM for initializing a mapping literal expression.
func (db *domBuilder) buildMappingInitializerExpression(mappingType typegraph.TypeReference, initializers map[string]codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	var entries = make([]codedom.ObjectLiteralEntryNode, 0)
	for name, expr := range initializers {
		entries = append(entries,
			codedom.ObjectLiteralEntryNode{
				codedom.LiteralValue(strconv.Quote(name), expr.BasisNode()),
				expr,
				expr.BasisNode(),
			})
	}

	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)
}
Ejemplo n.º 4
0
// AreEqual returns a call to the comparison operator between the two expressions.
func AreEqual(leftExpr Expression, rightExpr Expression, comparisonType typegraph.TypeReference, tdg *typegraph.TypeGraph, basis compilergraph.GraphNode) Expression {
	operator, found := comparisonType.ResolveMember("equals", typegraph.MemberResolutionOperator)
	if !found {
		panic(fmt.Sprintf("Unknown equals operator under type %v", comparisonType))
	}

	return MemberCall(
		StaticMemberReference(operator, comparisonType, basis),
		operator,
		[]Expression{leftExpr, rightExpr},
		basis)
}
Ejemplo n.º 5
0
// buildCollectionInitializerExpression builds a literal collection expression.
func (db *domBuilder) buildCollectionInitializerExpression(collectionType typegraph.TypeReference, valueExprs []codedom.Expression, emptyConstructorName string, arrayConstructorName string, node compilergraph.GraphNode) codedom.Expression {
	if len(valueExprs) == 0 {
		// Empty collection. Call the empty constructor directly.
		constructor, _ := collectionType.ResolveMember(emptyConstructorName, typegraph.MemberResolutionStatic)
		return codedom.MemberCall(
			codedom.MemberReference(codedom.TypeLiteral(collectionType, node), constructor, node),
			constructor,
			[]codedom.Expression{},
			node)
	}

	arrayExpr := codedom.ArrayLiteral(valueExprs, node)

	constructor, _ := collectionType.ResolveMember(arrayConstructorName, typegraph.MemberResolutionStatic)
	return codedom.MemberCall(
		codedom.MemberReference(codedom.TypeLiteral(collectionType, node), constructor, node),
		constructor,
		[]codedom.Expression{arrayExpr},
		node)
}
Ejemplo n.º 6
0
// ResolveStaticMember attempts to resolve a member (child) with the given name under this named scope, which
// must be Static.
func (nsi *namedScopeInfo) ResolveStaticMember(name string, module compilercommon.InputSource, staticType typegraph.TypeReference) (namedScopeInfo, error) {
	if !nsi.IsStatic() {
		return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under non-static scope", name)
	}

	if nsi.typeInfo != nil {
		typeMember, rerr := staticType.ResolveAccessibleMember(name, module, typegraph.MemberResolutionStatic)
		if rerr != nil {
			return namedScopeInfo{}, rerr
		}

		return namedScopeInfo{srg.SRGNamedScope{}, typeMember, nsi.sb}, nil
	} else {
		namedScopeOrImport, found := nsi.srgInfo.ResolveNameUnderScope(name)
		if !found {
			return namedScopeInfo{}, fmt.Errorf("Could not find static name '%v' under %v %v", name, nsi.srgInfo.Title(), nsi.srgInfo.Name())
		}

		return nsi.sb.processSRGNameOrInfo(namedScopeOrImport)
	}
}
Ejemplo n.º 7
0
// buildStructCloneExpression builds a clone expression for a struct type.
func (db *domBuilder) buildStructCloneExpression(structType typegraph.TypeReference, initializers map[string]codedom.Expression, node compilergraph.GraphNode) codedom.Expression {
	cloneMethod, found := structType.ResolveMember("Clone", typegraph.MemberResolutionInstance)
	if !found {
		panic(fmt.Sprintf("Missing Clone() method on type %v", structType))
	}

	cloneCall := codedom.MemberCall(
		codedom.MemberReference(
			db.getExpression(node, parser.NodeStructuralNewTypeExpression),
			cloneMethod,
			node),
		cloneMethod,
		[]codedom.Expression{},
		node)

	// If there are no initializers, then just return the cloned value directly.
	if len(initializers) == 0 {
		return cloneCall
	}

	return db.buildInitializationCompoundExpression(structType, initializers, cloneCall, node)
}
Ejemplo n.º 8
0
// InnerInstanceName returns the name of an inner instance of the given type, when accessed under a
// type instance which structurally composes it.
func (p Pather) InnerInstanceName(innerType typegraph.TypeReference) string {
	var name = unidecode.Unidecode(innerType.ReferredType().Name())
	if !innerType.HasGenerics() {
		return name
	}

	for _, generic := range innerType.Generics() {
		name = name + "$"
		name = name + p.InnerInstanceName(generic)
	}

	return name
}
Ejemplo n.º 9
0
// scopeReturnStatement scopes a return statement in the SRG.
func (sb *scopeBuilder) scopeReturnStatement(node compilergraph.GraphNode, context scopeContext) proto.ScopeInfo {
	var actualReturnType typegraph.TypeReference = sb.sg.tdg.VoidTypeReference()
	exprNode, found := node.TryGetNode(parser.NodeReturnStatementValue)
	if found {
		exprScope := sb.getScope(exprNode, context)
		if !exprScope.GetIsValid() {
			return newScope().
				Invalid().
				GetScope()
		}

		actualReturnType = exprScope.ResolvedTypeRef(sb.sg.tdg)
	}

	// Ensure the return types match.
	expectedReturnType, _ := sb.sg.tdg.LookupReturnType(context.parentImplemented)
	if expectedReturnType.IsVoid() {
		if !actualReturnType.IsVoid() {
			sb.decorateWithError(node, "No return value expected here, found value of type '%v'", actualReturnType)
			return newScope().
				Invalid().
				Returning(actualReturnType, true).
				GetScope()
		}
	} else if actualReturnType.IsVoid() {
		sb.decorateWithError(node, "Expected non-void resolved value")
		return newScope().
			Invalid().
			Returning(actualReturnType, true).
			GetScope()
	} else {
		if serr := actualReturnType.CheckSubTypeOf(expectedReturnType); serr != nil {
			sb.decorateWithError(node, "Expected return value of type '%v': %v", expectedReturnType, serr)
			return newScope().
				Invalid().
				Returning(actualReturnType, true).
				GetScope()
		}
	}

	return newScope().
		IsTerminatingStatement().
		Valid().
		Returning(actualReturnType, true).
		GetScope()
}
Ejemplo n.º 10
0
// scopeSmlNormalAttribute scopes an SML expression attribute under a declaration.
func (sb *scopeBuilder) scopeSmlAttribute(node compilergraph.GraphNode, propsType typegraph.TypeReference, context scopeContext) (string, bool) {
	attributeName := node.Get(parser.NodeSmlAttributeName)

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

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

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

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

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

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

	return attributeName, true
}
Ejemplo n.º 11
0
// scopeSmlDecoratorAttribute scopes a decorator SML expression attribute under a declaration.
func (sb *scopeBuilder) scopeSmlDecorator(node compilergraph.GraphNode, declaredType typegraph.TypeReference, context scopeContext) (typegraph.TypeReference, bool) {
	// Resolve the scope of the decorator.
	decoratorScope := sb.getScope(node.GetNode(parser.NodeSmlDecoratorPath), context)
	if !decoratorScope.GetIsValid() {
		return declaredType, false
	}

	namedScope, _ := sb.getNamedScopeForScope(decoratorScope)
	decoratorName := namedScope.Name()

	// Register that we make use of the decorator function.
	context.staticDependencyCollector.registerNamedDependency(namedScope)

	// Ensure the decorator refers to a function.
	decoratorType := decoratorScope.ResolvedTypeRef(sb.sg.tdg)
	if !decoratorType.IsDirectReferenceTo(sb.sg.tdg.FunctionType()) {
		sb.decorateWithError(node, "SML declaration decorator '%v' must refer to a function. Found: %v", decoratorName, decoratorType)
		return declaredType, false
	}

	// Ensure that the decorator doesn't return void.
	returnType := decoratorType.Generics()[0]
	if returnType.IsVoid() {
		sb.decorateWithError(node, "SML declaration decorator '%v' cannot return void", decoratorName)
		return declaredType, false
	}

	// Scope the attribute value (if any).
	var attributeValueType = sb.sg.tdg.BoolTypeReference()

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

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

	// Ensure the decorator takes the decorated type and a value as parameters.
	if decoratorType.ParameterCount() != 2 {
		sb.decorateWithError(node, "SML declaration decorator '%v' must refer to a function with two parameters. Found: %v", decoratorName, decoratorType)
		return returnType, false
	}

	// Ensure the first parameter is the declared type.
	allowedDecoratedType := decoratorType.Parameters()[0]
	allowedValueType := decoratorType.Parameters()[1]

	if serr := declaredType.CheckSubTypeOf(allowedDecoratedType); serr != nil {
		sb.decorateWithError(node, "SML declaration decorator '%v' expects to decorate an instance of type %v: %v", decoratorName, allowedDecoratedType, serr)
		return declaredType, false
	}

	// Ensure that the second parameter is the value type.
	if serr := attributeValueType.CheckSubTypeOf(allowedValueType); serr != nil {
		sb.decorateWithError(node, "Cannot assign value of type %v for decorator '%v': %v", attributeValueType, decoratorName, serr)
		return returnType, false
	}

	// The returned type is that of the decorator.
	return returnType, true
}
Ejemplo n.º 12
0
// TypeReferenceCall returns source for retrieving an object reference to the type defined by the given
// type reference.
func (p Pather) TypeReferenceCall(typeRef typegraph.TypeReference) string {
	if typeRef.IsAny() {
		return "$t.any"
	}

	if typeRef.IsStruct() {
		return "$t.struct"
	}

	if typeRef.IsNull() {
		return "$t.null"
	}

	if typeRef.IsVoid() {
		return "$t.void"
	}

	referredType := typeRef.ReferredType()
	if referredType.TypeKind() == typegraph.GenericType {
		return referredType.Name()
	}

	// Add the type name.
	typePath := p.GetTypePath(referredType)

	// If there are no generics, then simply return the type path.
	if !typeRef.HasGenerics() {
		return typePath
	}

	// Invoke the type with generics (if any).
	var genericsString = "("
	for index, generic := range typeRef.Generics() {
		if index > 0 {
			genericsString = genericsString + ", "
		}

		genericsString = genericsString + p.TypeReferenceCall(generic)
	}

	genericsString = genericsString + ")"
	return typePath + genericsString
}
Ejemplo n.º 13
0
// WithGenericType marks the scope as having the given generic type.
func (sib *scopeInfoBuilder) WithGenericType(generic typegraph.TypeReference) *scopeInfoBuilder {
	genericValue := generic.Value()
	sib.info.GenericType = &genericValue
	return sib
}
Ejemplo n.º 14
0
// WithStaticType marks the scope as having the given static type.
func (sib *scopeInfoBuilder) WithStaticType(static typegraph.TypeReference) *scopeInfoBuilder {
	staticValue := static.Value()
	sib.info.StaticType = &staticValue
	return sib
}
Ejemplo n.º 15
0
// Resolving marks the scope as resolving a value of the given type.
func (sib *scopeInfoBuilder) Resolving(resolved typegraph.TypeReference) *scopeInfoBuilder {
	resolvedValue := resolved.Value()
	sib.info.ResolvedType = &resolvedValue
	return sib
}
Ejemplo n.º 16
0
// Assignable marks the scope as being assignable with a value of the given type.
func (sib *scopeInfoBuilder) Assignable(assignable typegraph.TypeReference) *scopeInfoBuilder {
	assignableValue := assignable.Value()
	sib.info.AssignableType = &assignableValue
	return sib
}
Ejemplo n.º 17
0
// Returning marks the scope as returning a value of the given type.
func (sib *scopeInfoBuilder) Returning(returning typegraph.TypeReference, settlesScope bool) *scopeInfoBuilder {
	returnedValue := returning.Value()
	sib.info.ReturnedType = &returnedValue
	sib.info.IsSettlingScope = &settlesScope
	return sib
}
Ejemplo n.º 18
0
func (db *domBuilder) buildOptimizedBinaryOperatorExpression(node compilergraph.GraphNode, parentType typegraph.TypeReference, leftExpr codedom.Expression, rightExpr codedom.Expression) (codedom.Expression, bool) {
	// Verify this is a supported native operator.
	opString, hasOp := operatorMap[node.Kind()]
	if !hasOp {
		return nil, false
	}

	// Verify we have a native binary operator we can optimize.
	if !parentType.IsNominal() {
		return nil, false
	}

	isNumeric := false

	switch {
	case parentType.IsDirectReferenceTo(db.scopegraph.TypeGraph().IntType()):
		isNumeric = true

	case parentType.IsDirectReferenceTo(db.scopegraph.TypeGraph().BoolType()):
		fallthrough

	case parentType.IsDirectReferenceTo(db.scopegraph.TypeGraph().StringType()):
		fallthrough

	default:
		return nil, false
	}

	// Handle the various kinds of operators.
	switch node.Kind() {
	case parser.NodeComparisonEqualsExpression:
		fallthrough

	case parser.NodeComparisonNotEqualsExpression:
		// Always allowed.
		break

	case parser.NodeComparisonLTEExpression:
		fallthrough
	case parser.NodeComparisonLTExpression:
		fallthrough
	case parser.NodeComparisonGTEExpression:
		fallthrough
	case parser.NodeComparisonGTExpression:
		// Only allowed for number.
		if !isNumeric {
			return nil, false
		}
	}

	boolType := db.scopegraph.TypeGraph().BoolTypeReference()

	unwrappedLeftExpr := codedom.NominalUnwrapping(leftExpr, parentType, node)
	unwrappedRightExpr := codedom.NominalUnwrapping(rightExpr, parentType, node)

	compareExpr := codedom.BinaryOperation(unwrappedLeftExpr, opString, unwrappedRightExpr, node)
	return codedom.NominalRefWrapping(compareExpr,
		boolType.NominalDataType(),
		boolType,
		node), true
}
Ejemplo n.º 19
0
// appendSigReference appends the given type reference to the given signature code
// buffer, returning true if any portion is dynamic.
func appendSigReference(typeRef typegraph.TypeReference, buf *bytes.Buffer) bool {
	if typeRef.IsAny() {
		buf.WriteString("any")
		return false
	}

	if typeRef.IsStruct() {
		buf.WriteString("struct")
		return false
	}

	if typeRef.IsNull() {
		buf.WriteString("null")
		return false
	}

	if typeRef.IsVoid() {
		buf.WriteString("void")
		return false
	}

	referredType := typeRef.ReferredType()
	if referredType.TypeKind() == typegraph.GenericType {
		buf.WriteString("\" + $t.typeid(")
		buf.WriteString(referredType.Name())
		buf.WriteString(") + \"")
		return true
	}

	// Add the type's unique ID.
	buf.WriteString(referredType.GlobalUniqueId())

	// If there are no generics, then we're done.
	if !typeRef.HasGenerics() {
		return false
	}

	// Otherwise, append the generics.
	buf.WriteRune('<')

	var dynamic = false
	for index, generic := range typeRef.Generics() {
		if index > 0 {
			buf.WriteRune(',')
		}

		genericDynamic := appendSigReference(generic, buf)
		dynamic = dynamic || genericDynamic
	}

	buf.WriteRune('>')
	return dynamic
}