// 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) }
// buildSlicerExpression builds the CodeDOM for a slice call. func (db *domBuilder) buildSlicerExpression(node compilergraph.GraphNode) codedom.Expression { childExpr := db.getExpression(node, parser.NodeSliceExpressionChildExpr) leftExpr := db.getExpressionOrDefault(node, parser.NodeSliceExpressionLeftIndex, codedom.LiteralValue("null", node)) rightExpr := db.getExpressionOrDefault(node, parser.NodeSliceExpressionRightIndex, codedom.LiteralValue("null", node)) scope, _ := db.scopegraph.GetScope(node) operator, _ := scope.CalledOperator(db.scopegraph.TypeGraph()) opExpr := codedom.MemberReference(childExpr, operator, node) return codedom.MemberCall(opExpr, operator, []codedom.Expression{leftExpr, rightExpr}, node) }
// generateShortCircuitedBinaryOperator generates the expression source for a short circuiting binary operator. func (eg *expressionGenerator) generateShortCircuitedBinaryOperator(binaryOp *codedom.BinaryOperationNode, context generationContext) esbuilder.ExpressionBuilder { compareValue := codedom.LiteralValue("false", binaryOp.BasisNode()) if binaryOp.Operator == "&&" { compareValue = codedom.LiteralValue("true", binaryOp.BasisNode()) } return eg.generateShortCircuiter( binaryOp.LeftExpr, compareValue, binaryOp.RightExpr, context, func(resultName string, rightSide esbuilder.ExpressionBuilder) esbuilder.ExpressionBuilder { return esbuilder.Binary(esbuilder.Identifier(resultName), binaryOp.Operator, rightSide) }) }
// 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) }
// 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) }
// 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) }
// 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, ) }
// buildVarStatement builds the CodeDOM for a variable statement. func (db *domBuilder) buildVarStatement(node compilergraph.GraphNode) codedom.Statement { name := node.Get(parser.NodeVariableStatementName) initExpr, _ := db.tryGetExpression(node, parser.NodeVariableStatementExpression) if initExpr == nil { // If no init expression was specified, then the variable is initialized to null. initExpr = codedom.LiteralValue("null", node) } return codedom.VarDefinitionWithInit(name, initExpr, node) }
// generateNullComparisonOperator generates the expression for a null comparison operator. func (eg *expressionGenerator) generateNullComparisonOperator(compareOp *codedom.BinaryOperationNode, context generationContext) esbuilder.ExpressionBuilder { return eg.generateShortCircuiter( compareOp.LeftExpr, codedom.LiteralValue("null", compareOp.BasisNode()), compareOp.RightExpr, context, func(resultName string, rightSide esbuilder.ExpressionBuilder) esbuilder.ExpressionBuilder { return esbuilder.Call(esbuilder.Snippet(string(codedom.NullableComparisonFunction)), esbuilder.Identifier(resultName), rightSide) }) }
// 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) }
// 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) }
// 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) }
// 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) }
// buildSwitchStatement builds the CodeDOM for a switch statement. func (db *domBuilder) buildSwitchStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) { var switchVarName = "" var switchVarNode = node var switchType = db.scopegraph.TypeGraph().BoolTypeReference() // Start with an empty statement. This will be replaced if we have a switch-level expression. var startStatement codedom.Statement = codedom.EmptyStatement(node) // Check for a switch-level expression. If not present, then the switch compares each case // as a boolean value. If present, then we compare the case branches to this expression, // which is placed into a new variable. switchExpr, hasSwitchExpr := db.tryGetExpression(node, parser.NodeSwitchStatementExpression) if hasSwitchExpr { switchVarName = db.generateScopeVarName(node) switchVarNode = node.GetNode(parser.NodeSwitchStatementExpression) startStatement = codedom.VarDefinitionWithInit(switchVarName, switchExpr, node) switchScope, _ := db.scopegraph.GetScope(switchVarNode) switchType = switchScope.ResolvedTypeRef(db.scopegraph.TypeGraph()) } getCheckExpression := func(caseExpressionNode compilergraph.GraphNode) codedom.Expression { caseExpression := db.buildExpression(caseExpressionNode) // If no switch-level expression, then the expression being checked is the case // expression itself, which is guarenteed to be a boolean expression. if !hasSwitchExpr { return caseExpression } // Otherwise, we check if the case's expression is equal to the value of the switch-level // expression, as found in the variable in which we placed it before the switch runs. return codedom.AreEqual( codedom.LiteralValue(switchVarName, caseExpressionNode), caseExpression, switchType, db.scopegraph.TypeGraph(), caseExpressionNode) } return db.buildJumpingCaseStatement(node, parser.NodeSwitchStatementCase, parser.NodeSwitchStatementCaseStatement, parser.NodeSwitchStatementCaseExpression, startStatement, getCheckExpression) }
// 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, }) }
// buildNumericLiteral builds the CodeDOM for a numeric literal. func (db *domBuilder) buildNumericLiteral(node compilergraph.GraphNode) codedom.Expression { numericValueStr := node.Get(parser.NodeNumericLiteralExpressionValue) if strings.HasSuffix(numericValueStr, "f") { numericValueStr = numericValueStr[0 : len(numericValueStr)-1] } // Handle binary. if strings.HasPrefix(numericValueStr, "0b") || strings.HasPrefix(numericValueStr, "0B") { parsed, _ := strconv.ParseInt(numericValueStr[2:], 2, 64) numericValueStr = strconv.Itoa(int(parsed)) } // Note: Handles Hex. intValue, isNotInt := strconv.ParseInt(numericValueStr, 0, 64) if isNotInt == nil { numericValueStr = strconv.Itoa(int(intValue)) } exprScope, _ := db.scopegraph.GetScope(node) numericType := exprScope.ResolvedTypeRef(db.scopegraph.TypeGraph()).ReferredType() return codedom.NominalWrapping(codedom.LiteralValue(numericValueStr, node), numericType, node) }
// 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 }
// buildBooleanLiteral builds the CodeDOM for a boolean literal. func (db *domBuilder) buildBooleanLiteral(node compilergraph.GraphNode) codedom.Expression { booleanValueStr := node.Get(parser.NodeBooleanLiteralExpressionValue) return codedom.NominalWrapping(codedom.LiteralValue(booleanValueStr, node), db.scopegraph.TypeGraph().BoolType(), node) }
// buildNullLiteral builds the CodeDOM for a null literal. func (db *domBuilder) buildNullLiteral(node compilergraph.GraphNode) codedom.Expression { return codedom.LiteralValue("null", node) }
// buildJumpingCaseStatement builds the CodeDOM for a statement which jumps (branches) based on // various cases. func (db *domBuilder) buildJumpingCaseStatement(node compilergraph.GraphNode, casePredicate compilergraph.Predicate, caseStatementPredicate compilergraph.Predicate, caseExpressionPredicate compilergraph.Predicate, startStatement codedom.Statement, getCheckExpression checkExpressionGenerator) (codedom.Statement, codedom.Statement) { // A branching statement is an extended version of a conditional statement. For each branch, we check if the // branch's value matches the conditional (or "true" if there is no conditional expr). On true, the block // under the case is executed, followed by a jump to the final statement. Otherwise, the next block // in the chain is executed. finalStatement := codedom.EmptyStatement(node) // Save a break statement reference for the generated statement. db.breakStatementMap[node.NodeId] = finalStatement // Generate the statements and check expressions for each of the cases. var branchJumps = make([]*codedom.ConditionalJumpNode, 0) cit := node.StartQuery(). Out(casePredicate). BuildNodeIterator() for cit.Next() { caseNode := cit.Node() caseStart, caseEnd := db.getStatements(caseNode, caseStatementPredicate) caseExpressionNode, hasCaseExpression := caseNode.TryGetNode(caseExpressionPredicate) // Generate the expression against which we should check. If there is no expression on // the case, then this is the default and we compare against "true". var branchExpression codedom.Expression = nil if hasCaseExpression { branchExpression = getCheckExpression(caseExpressionNode) } else { branchExpression = codedom.NominalWrapping( codedom.LiteralValue("true", caseNode), db.scopegraph.TypeGraph().BoolType(), caseNode) } branchJump := codedom.BranchOn(branchExpression, caseNode) branchJump.True = caseStart branchJumps = append(branchJumps, branchJump) // Jump the end statement, once complete, to the final statement. codedom.AssignNextStatement(caseEnd, codedom.UnconditionalJump(finalStatement, caseNode)) } // Generate a "trampoline" that checks each case. for index, branchJump := range branchJumps { // Jump the current branch (on false) to the next conditional check. if index < len(branchJumps)-1 { branchJump.False = branchJumps[index+1] } else { branchJump.False = finalStatement } } // Have the start state jump to the first branch (if any). if len(branchJumps) > 0 { codedom.AssignNextStatement(startStatement, branchJumps[0]) } return startStatement, finalStatement }
// buildExpression builds the CodeDOM for the given SRG node and returns it as an expression. Will // panic if the returned DOM type is not an expression. func (db *domBuilder) buildExpression(node compilergraph.GraphNode) codedom.Expression { switch node.Kind() { // Access Expressions. case parser.NodeMemberAccessExpression: fallthrough case parser.NodeNullableMemberAccessExpression: fallthrough case parser.NodeDynamicMemberAccessExpression: return db.buildMemberAccessExpression(node) case parser.NodeTypeIdentifierExpression: return db.buildIdentifierExpression(node) case parser.NodeGenericSpecifierExpression: return db.buildGenericSpecifierExpression(node) case parser.NodeCastExpression: return db.buildCastExpression(node) case parser.NodeStreamMemberAccessExpression: return db.buildStreamMemberAccessExpression(node) // Await Expression. case parser.NodeTypeAwaitExpression: return db.buildAwaitExpression(node) // Lambda Expressions. case parser.NodeTypeLambdaExpression: return db.buildLambdaExpression(node) // SML Expressions. case parser.NodeTypeSmlExpression: return db.buildSmlExpression(node) case parser.NodeTypeSmlText: return db.buildSmlText(node) // Flow Expressions. case parser.NodeTypeConditionalExpression: return db.buildConditionalExpression(node) case parser.NodeTypeLoopExpression: return db.buildLoopExpression(node) // Op Expressions. case parser.NodeRootTypeExpression: return db.buildRootTypeExpression(node) case parser.NodeFunctionCallExpression: return db.buildFunctionCall(node) case parser.NodeSliceExpression: return db.buildSliceExpression(node) case parser.NodeNullComparisonExpression: return db.buildNullComparisonExpression(node) case parser.NodeAssertNotNullExpression: return db.buildAssertNotNullExpression(node) case parser.NodeIsComparisonExpression: return db.buildIsComparisonExpression(node) case parser.NodeInCollectionExpression: return db.buildInCollectionExpression(node) case parser.NodeBitwiseNotExpression: return db.buildUnaryOperatorExpression(node, nil) case parser.NodeKeywordNotExpression: return db.buildUnaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.UnaryOperation("!", codedom.NominalUnwrapping(expr, boolType, node), node) return codedom.NominalWrapping( childExpr, db.scopegraph.TypeGraph().BoolType(), node) }) case parser.NodeDefineRangeExpression: fallthrough case parser.NodeBitwiseXorExpression: fallthrough case parser.NodeBitwiseOrExpression: fallthrough case parser.NodeBitwiseAndExpression: fallthrough case parser.NodeBitwiseShiftLeftExpression: fallthrough case parser.NodeBitwiseShiftRightExpression: fallthrough case parser.NodeBinaryAddExpression: fallthrough case parser.NodeBinarySubtractExpression: fallthrough case parser.NodeBinaryMultiplyExpression: fallthrough case parser.NodeBinaryDivideExpression: fallthrough case parser.NodeBinaryModuloExpression: return db.buildBinaryOperatorExpression(node, nil) case parser.NodeComparisonEqualsExpression: return db.buildBinaryOperatorExpression(node, nil) case parser.NodeComparisonNotEqualsExpression: return db.buildBinaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.UnaryOperation("!", codedom.NominalUnwrapping(expr, boolType, node), node) return codedom.NominalRefWrapping( childExpr, boolType.NominalDataType(), boolType, node) }) case parser.NodeComparisonLTEExpression: return db.buildBinaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { intType := db.scopegraph.TypeGraph().IntTypeReference() boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.BinaryOperation(codedom.NominalUnwrapping(expr, intType, node), "<=", codedom.LiteralValue("0", node), node) return codedom.NominalRefWrapping( childExpr, boolType.NominalDataType(), boolType, node) }) case parser.NodeComparisonLTExpression: return db.buildBinaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { intType := db.scopegraph.TypeGraph().IntTypeReference() boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.BinaryOperation(codedom.NominalUnwrapping(expr, intType, node), "<", codedom.LiteralValue("0", node), node) return codedom.NominalRefWrapping( childExpr, boolType.NominalDataType(), boolType, node) }) case parser.NodeComparisonGTEExpression: return db.buildBinaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { intType := db.scopegraph.TypeGraph().IntTypeReference() boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.BinaryOperation(codedom.NominalUnwrapping(expr, intType, node), ">=", codedom.LiteralValue("0", node), node) return codedom.NominalRefWrapping( childExpr, boolType.NominalDataType(), boolType, node) }) case parser.NodeComparisonGTExpression: return db.buildBinaryOperatorExpression(node, func(expr codedom.Expression) codedom.Expression { intType := db.scopegraph.TypeGraph().IntTypeReference() boolType := db.scopegraph.TypeGraph().BoolTypeReference() childExpr := codedom.BinaryOperation(codedom.NominalUnwrapping(expr, intType, node), ">", codedom.LiteralValue("0", node), node) return codedom.NominalRefWrapping( childExpr, boolType.NominalDataType(), boolType, node) }) // Boolean operators. case parser.NodeBooleanAndExpression: return db.buildBooleanBinaryExpression(node, "&&") case parser.NodeBooleanOrExpression: return db.buildBooleanBinaryExpression(node, "||") case parser.NodeBooleanNotExpression: return db.buildBooleanUnaryExpression(node, "!") // Literals. case parser.NodeStructuralNewExpression: return db.buildStructuralNewExpression(node) case parser.NodeNumericLiteralExpression: return db.buildNumericLiteral(node) case parser.NodeBooleanLiteralExpression: return db.buildBooleanLiteral(node) case parser.NodeStringLiteralExpression: return db.buildStringLiteral(node) case parser.NodeNullLiteralExpression: return db.buildNullLiteral(node) case parser.NodeThisLiteralExpression: return db.buildThisLiteral(node) case parser.NodeValLiteralExpression: return db.buildValLiteral(node) case parser.NodeListExpression: return db.buildListExpression(node) case parser.NodeSliceLiteralExpression: return db.buildSliceLiteralExpression(node) case parser.NodeMappingLiteralExpression: return db.buildMappingLiteralExpression(node) case parser.NodeMapExpression: return db.buildMapExpression(node) case parser.NodeTypeTemplateString: return db.buildTemplateStringExpression(node) case parser.NodeTaggedTemplateLiteralString: return db.buildTaggedTemplateString(node) default: panic(fmt.Sprintf("Unknown SRG expression node: %s", node.Kind())) return nil } }
// buildSmlText builds the CodeDOM for a SML text literal. func (db *domBuilder) buildSmlText(node compilergraph.GraphNode) codedom.Expression { stringValueStr := strconv.Quote(node.Get(parser.NodeSmlTextValue)) return codedom.NominalWrapping(codedom.LiteralValue(stringValueStr, node), db.scopegraph.TypeGraph().StringType(), node) }