public async Task advent_glulx_start_function() { #region code //label_0: // call 48 () // return 0"; #endregion using (var stream = Resources.LoadResource(Resources.Glulx_Advent)) { var machine = await GlulxMachine.CreateAsync(stream); var graph = ControlFlowGraph.Compute(machine.StartFunction.Body); Assert.Equal(3, graph.Blocks.Count); Assert.Single(graph.EntryBlock.Successors); Assert.Empty(graph.EntryBlock.Predecessors); Assert.Empty(graph.ExitBlock.Successors); Assert.Single(graph.ExitBlock.Predecessors); VerifyLabel(0, graph, Predecessors(Entry), Successors(Exit), StatementCount(3)); } }
public uint Evaluate(Function function) { var cfg = ControlFlowGraph.Compute(function.Body); var block = cfg.GetBlock(cfg.EntryBlock.Successors[0]); var result = 0u; while (!block.IsExit) { var nextBlockId = block.ID.GetNext(); foreach (var statement in block.Statements) { var jump = false; // First, handle any control flow statements void HandleReturnStatement(AstReturnStatement returnStatement) { result = Evaluate(returnStatement.Expression); nextBlockId = BlockId.Exit; jump = true; } void HandleJumpStatement(AstJumpStatement jumpStatement) { nextBlockId = new BlockId(jumpStatement.Label.Index); jump = true; } switch (statement.Kind) { case AstNodeKind.ReturnStatement: HandleReturnStatement((AstReturnStatement)statement); break; case AstNodeKind.JumpStatement: HandleJumpStatement((AstJumpStatement)statement); break; case AstNodeKind.BranchStatement: { var branchStatement = (AstBranchStatement)statement; var condition = Evaluate(branchStatement.Condition); if (condition == 1) { switch (branchStatement.Statement.Kind) { case AstNodeKind.ReturnStatement: HandleReturnStatement((AstReturnStatement)branchStatement.Statement); break; case AstNodeKind.JumpStatement: HandleJumpStatement((AstJumpStatement)branchStatement.Statement); break; default: throw new InvalidOperationException($"Invalid statement kind for branch: {branchStatement.Statement.Kind}"); } } continue; } } if (jump) { break; } Evaluate(statement); } block = cfg.GetBlock(nextBlockId); } return(result); }
public async Task glulxercise_glulx_function_48_has_correct_body() { #region code //label_0: // call fb () // local_0 <- 74a // push call 1c8bd (28c95, 0) // if (pop != 4d2) then // jump label_4 //label_1: // push call 1c8bd (28c95, 1) // if (pop != 2) then // jump label_4 //label_2: // push call 1c8bd (28c95, 2) // if (pop != 0) then // jump label_4 //label_3: // output-char a // output-string 1d122 // call 1c93c (28c95, 1, 3) // call 1c93c (28c95, 2, 3) // output-string 1d147 // restore-undo // output-string 1d156 // output-num local_0 // output-string 1d161 // quit //label_4: // output-char a // call 4e9 () // output-char a // call e61 () // call 672 () // output-string 1d16d // output-string 1d1a7 // return 1 #endregion using (var stream = Resources.LoadResource(Resources.Glulx_Glulxercise)) { var machine = await GlulxMachine.CreateAsync(stream); var function = machine.GetFunction(0x48); var graph = ControlFlowGraph.Compute(function.Body); Assert.Equal(7, graph.Blocks.Count); Assert.Single(graph.EntryBlock.Successors); Assert.Empty(graph.EntryBlock.Predecessors); Assert.Empty(graph.ExitBlock.Successors); Assert.Equal(2, graph.ExitBlock.Predecessors.Count); VerifyLabel(0, graph, Predecessors(Entry), Successors(1, 4), StatementCount(5)); VerifyLabel(1, graph, Predecessors(0), Successors(2, 4), StatementCount(3)); VerifyLabel(2, graph, Predecessors(1), Successors(3, 4), StatementCount(3)); VerifyLabel(3, graph, Predecessors(2), Successors(Exit), StatementCount(11)); VerifyLabel(4, graph, Predecessors(0, 1, 2), Successors(Exit), StatementCount(9)); } }