TwistAndShutter
TwistAndShutter

Reputation: 239

How to resume a method from its last return point?

I wish to know if there is some kind of mechanism in C# which allows to "resume" a method from the last return that it reached during the its last invocation.

WHAT I NEED: I have an Abstract Syntax Tree (AST) which is created by a Parser designed from a source language. This Abstract Syntax Tree is an object of a "root class" which have as fields instances of other classes and so on. What I need to do is to create a sequencer which takes this Abstract Syntax Tree and through a generator creates instructions in other language. The instructions sequence terminates with a null. This is possible through a generator method next(), which invokes a sequencer method which computes on-the-fly the next instruction. In other words, I can't explore the whole Abstract Syntax Tree, generates all the instructions and return them one-by-one every time that I call next(), but I MUST create each one of them every time that the generator call next().

AN EXAMPLE: I will post a pseudo code in order to make you better understand the problem.

static void Main(string[] args)
{Parser parser = new Parser (//constructor stuff);
 Sequencer sequencer = new Sequencer(parser.Parse()); //the mehtod Parse() generates the AST
 Generator generator = new Generator(sequencer);
 Instruction instruction = generator.next();
 while(instruction)
      {print instruction
       instruction = generator.next();
      } 
}

IMPORTANT NOTE: the most important thing that I wish that you understand is that next() is not always called inside a some kind of iteration, so I don't think that foreach and iterators are a good solution.

This because in order to use iterotors I have finally to write something like

foreach(instruction in next())
       {//do stuff with instruction}

And I don't want to do that!

However, I will make you see how next() should be structured:

Instruction next()
{ return sequencer.generate();}

And so generate():

Instruction generate():
{Insturction instr;
while(I explored the whole AST)
      {if(//actual node in the AST is this kind)
          instr=//generate this instruction
      else if(//actual node in the AST is this other kind)
          instr=//generate this other instruction
      else and so on....
      //if the actual node has some child, then it is new actual node
      **yield return** instruction;
      }
 } 

The most complicate part is that I need something with a yield return behaviour (so starting where I left at the next generate() calling, but without using iterator for the reasons that I explained before. In addiction to that, it is extremely difficult to move inside the AST since I cannot have an explicit reference of the parent of the actual node (like a field which is a copy of the actual node).

As it is not sufficient, you can recursively call generate (for example if there is some kind of iterative constructor to translate).

How to achieve this?

Upvotes: 0

Views: 197

Answers (2)

BartoszKP
BartoszKP

Reputation: 35901

In fact, yield, along with the IEnumerator<T> do satisfy your requirements. The key point to note is that IEnumerable<T> exposes the GetEnumerator method, which is exactly what you need here.

Generator:

public class Generator
{
    //...

    public IEnumerable<Instruction> Generate()
    {
        // ...
        yield return new Instruction(...);
        // ...
    }
    //...
}

An example on how to use it the way you want. The key part is that you can do generator.Generate().GetEnumerator();:

var enumerator = generator.Generate().GetEnumerator();
while (enumerator.MoveNext())
{
    var instruction = enumerator.Current;
    // do something with instruction

    if (steps > 10)  //some dummy reason to stop ordinary iteration
        break;
}

// Now process generator's results manually

if (!enumerator.MoveNext())
    throw new InstructionMissingException();   // no more instructions left

var followingInstruction = enumerator.Current;

// ...

if (!enumerator.MoveNext())
    throw new InstructionMissingException();   // no more instructions left

var theLastInstruction = enumerator.Current;

// ...

if (enumerator.MoveNext())
    throw new TooMuchInstructionsException(); // unexpected instruction

I've noticed that GetEnumerator can be called on IEnumerable thanks to this answer which answers a similar question.

Additionally, as Alexei Levenkov points out in his comment you can wrap MoveNext and Current with a comfortable method that suits your needs more accurately. You could even write an extension method for the IEnumerator<Instruction> type:

public static class IEnumeratorExtensions
{
    public static Instruction NextInstruction(this IEnumerator<Instruction> @this)
    {
        if (@this.MoveNext())
        {
            return @this.Current;
        }

        return null; // or throw, or whatever you like
    }
}

Upvotes: 1

Ben Voigt
Ben Voigt

Reputation: 283783

Use yield return to implement the main logic. That'll create an enumerator. Store it in a class member variable or somewhere else permanent.

Then use that enumerator inside a wrapper method that returns the plain object, pulling one new item from the enumerator on each call.

Upvotes: 3

Related Questions