Пример #1
0
// buildContinueStatement builds the CodeDOM for a continue statement.
func (db *domBuilder) buildContinueStatement(node compilergraph.GraphNode) codedom.Statement {
	// Find the parent loop statement (guarenteed to be there due to scope graph constraints).
	scope, _ := db.scopegraph.GetScope(node)
	parentNode, _ := scope.TargetedNode(db.scopegraph.SourceGraph())

	// Add a jump to the continue state for the parent.
	return codedom.UnconditionalJump(db.continueStatementMap[parentNode.NodeId], node)
}
Пример #2
0
// buildConditionalStatement builds the CodeDOM for a conditional statement.
func (db *domBuilder) buildConditionalStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	conditionalExpr := db.getExpression(node, parser.NodeConditionalStatementConditional)

	// A conditional is buildd as a jump statement that jumps to the true statement when the
	// expression is true and to the false statement (which may have an 'else') when it is false.
	// Both statements then jump to a final statement once complete.
	finalStatement := codedom.EmptyStatement(node)
	trueStart, trueEnd := db.getStatements(node, parser.NodeConditionalStatementBlock)

	// The true statement needs to jump to the final statement once complete.
	codedom.AssignNextStatement(trueEnd, codedom.UnconditionalJump(finalStatement, node))

	elseStart, elseEnd, hasElse := db.tryGetStatements(node, parser.NodeConditionalStatementElseClause)
	if hasElse {
		// The else statement needs to jump to the final statement once complete.
		codedom.AssignNextStatement(elseEnd, codedom.UnconditionalJump(finalStatement, node))

		return codedom.ConditionalJump(conditionalExpr, trueStart, elseStart, node), finalStatement
	} else {
		return codedom.ConditionalJump(conditionalExpr, trueStart, finalStatement, node), finalStatement
	}
}
Пример #3
0
// 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
}
Пример #4
0
// buildLoopStatement builds the CodeDOM for a loop statement.
func (db *domBuilder) buildLoopStatement(node compilergraph.GraphNode) (codedom.Statement, codedom.Statement) {
	startStatement := codedom.EmptyStatement(node)
	startStatement.MarkReferenceable()

	finalStatement := codedom.EmptyStatement(node)

	// Save initial continue and break statements for the loop.
	db.continueStatementMap[node.NodeId] = startStatement
	db.breakStatementMap[node.NodeId] = finalStatement

	// A loop statement is buildd as a start statement which conditionally jumps to either the loop body
	// (on true) or, on false, jumps to a final state after the loop.
	if loopExpr, hasLoopExpr := db.tryGetExpression(node, parser.NodeLoopStatementExpression); hasLoopExpr {
		// Check for a named value under the loop. If found, this is a loop over a stream or streamable.
		namedValue, hasNamedValue := node.TryGetNode(parser.NodeStatementNamedValue)
		if hasNamedValue {
			namedValueName := namedValue.Get(parser.NodeNamedValueName)
			resultVarName := db.generateScopeVarName(node)

			namedValueScope, _ := db.scopegraph.GetScope(namedValue)

			// Create the stream variable.
			streamVarName := db.generateScopeVarName(node)
			var streamVariable = codedom.VarDefinitionWithInit(streamVarName, loopExpr, node)

			// If the expression is Streamable, first call .Stream() on the expression to get the stream
			// for the variable.
			if namedValueScope.HasLabel(proto.ScopeLabel_STREAMABLE_LOOP) {
				// Call .Stream() on the expression.
				streamableMember, _ := db.scopegraph.TypeGraph().StreamableType().GetMember("Stream")
				streamExpr := codedom.MemberCall(
					codedom.MemberReference(loopExpr, streamableMember, namedValue),
					streamableMember,
					[]codedom.Expression{},
					namedValue)

				streamVariable = codedom.VarDefinitionWithInit(streamVarName, streamExpr, node)
			}

			// Create variables to hold the named value (as requested in the SRG) and the loop result.
			namedVariable := codedom.VarDefinition(namedValueName, node)
			resultVariable := codedom.VarDefinition(resultVarName, node)

			// Create an expression statement to set the result variable to a call to Next().
			streamMember, _ := db.scopegraph.TypeGraph().StreamType().GetMember("Next")
			nextCallExpr := codedom.MemberCall(
				codedom.MemberReference(codedom.LocalReference(streamVarName, node), streamMember, node),
				streamMember,
				[]codedom.Expression{},
				namedValue)

			resultExpressionStatement := codedom.ExpressionStatement(codedom.LocalAssignment(resultVarName, nextCallExpr, namedValue), namedValue)
			resultExpressionStatement.MarkReferenceable()

			// Set the continue statement to call Next() again.
			db.continueStatementMap[node.NodeId] = resultExpressionStatement

			// Create an expression statement to set the named variable to the first part of the tuple.
			namedExpressionStatement := codedom.ExpressionStatement(
				codedom.LocalAssignment(namedValueName,
					codedom.NativeAccess(
						codedom.LocalReference(resultVarName, namedValue), "First", namedValue),
					namedValue),
				namedValue)

			// Jump to the body state if the second part of the tuple in the result variable is true.
			bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock)

			checkJump := codedom.ConditionalJump(
				codedom.NativeAccess(codedom.LocalReference(resultVarName, node), "Second", node),
				bodyStart,
				finalStatement,
				node)

			// Steps:
			// 1) Empty statement
			// 2) Create the stream's variable (with no value)
			// 3) Create the named value variable (with no value)
			// 4) Create the result variable (with no value)
			// 5) (loop starts here) Pull the Next() result out of the stream
			// 6) Pull the true/false bool out of the result
			// 7) Pull the named value out of the result
			// 8) Jump based on the true/false boolean value
			codedom.AssignNextStatement(startStatement, streamVariable)
			codedom.AssignNextStatement(streamVariable, namedVariable)
			codedom.AssignNextStatement(namedVariable, resultVariable)
			codedom.AssignNextStatement(resultVariable, resultExpressionStatement)
			codedom.AssignNextStatement(resultExpressionStatement, namedExpressionStatement)
			codedom.AssignNextStatement(namedExpressionStatement, checkJump)

			// Jump to the result checking expression once the loop body completes.
			directJump := codedom.UnconditionalJump(resultExpressionStatement, node)
			codedom.AssignNextStatement(bodyEnd, directJump)

			return startStatement, finalStatement
		} else {
			bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock)

			// Loop over a direct boolean expression which is evaluated on each iteration.
			initialJump := codedom.ConditionalJump(loopExpr, bodyStart, finalStatement, node)
			directJump := codedom.UnconditionalJump(initialJump, node)

			codedom.AssignNextStatement(bodyEnd, directJump)
			codedom.AssignNextStatement(startStatement, initialJump)

			return startStatement, finalStatement
		}
	} else {
		bodyStart, bodyEnd := db.getStatements(node, parser.NodeLoopStatementBlock)

		// A loop without an expression just loops infinitely over the body.
		directJump := codedom.UnconditionalJump(bodyStart, node)

		codedom.AssignNextStatement(bodyEnd, directJump)
		codedom.AssignNextStatement(startStatement, bodyStart)

		return startStatement, directJump
	}
}