0

I asked a question earlier, but I don't think I was clear enough about the sort of answers I was hoping for, so let me provide a more concrete example:

class Program
{
    private static bool State;

    static void Main(string[] args)
    {
        State = false;
        Console.WriteLine(And());
        Console.ReadLine();
    }

    static bool And()
    {
        return Or() && C();
    }

    static bool Or()
    {
        return A() || AB();
    }

    static bool C()
    {
        return State;
    }

    static bool A()
    {
        return true;
    }

    static bool AB()
    {
        State = true;
        return true;
    }
}

The flow of this program looks like:

  1. And() gets called
  2. And() calls Or()
  3. Or() calls A()
  4. A() returns true
  5. Flow returns to Or(), which returns true (lazy evaluation)
  6. Flow returns to And(), And() calls C()
  7. C() returns false
  8. Flow returns to And(), which returns false

Now if Or() did not perform lazy evaluation (I change || to |), the program will return true. However, I don't want AB() to executed unless the result of the entire parse fails (And() returns false).

So what I'd like to do, is somewhere in the Or() function, save the current state on a stack (static variable) so that if And() returns false, I can pop an item off the stack and try the alternative.

How would I accomplish this in C#?

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Personally, I would just using something like GPPG and be done with it. – leppie Mar 17 '12 at 03:37
  • This is not possible in C#, you cannot capture the cpu call stack and restore it later. – Hans Passant Mar 17 '12 at 03:49
  • @leppie: Gardens Point Parser Generator? I'll look into it, but I'd still like to do this as a learning exercise. – mpen Mar 17 '12 at 04:11
  • @HansPassant: How do we work around that fact then? – mpen Mar 17 '12 at 04:12
  • Creating parsers is quite possible in C#, just not the way you envision it. Nor is this restriction unique to C#, you can't do it in C++ either. You got excellent answers in your previous question. – Hans Passant Mar 17 '12 at 04:20
  • @HansPassant: I'm not saying they were bad answers, I was just hoping I wouldn't have to stray too far from what I've got so far and start back at square 1. I know it's not a problem unique to C#, I'm just trying to wrap my head around how I can overcome this in a preferably clean and simple way. Just looking for the "next step" to get over this hurdle. – mpen Mar 17 '12 at 04:25
  • You might want to try getting all your facts together before you start writing your code. It seems you've created a design that can't do what you need it to do. – John Saunders Mar 17 '12 at 04:37
  • @JohnSaunders: That sounded like a jab. Sometimes all the requirements aren't immediately apparent until you get into it. When I started out I was able to parse simple strings just fine; it wasn't until the language evolved a bit and realized I needed backtracking that I ran into problems. I don't think there's any fault in jumping right in to figure out what works and what doesn't. I can see that my design won't work as is; that's why I'm asking how I can fix it. – mpen Mar 17 '12 at 04:40
  • But you haven't been asking how to fix your design; you've been asking how to make the code written with the earlier design do what the new requirements are demanding. This may be one of those times when you need to redesign. You'll often find that you can reuse existing code once you do that, but I would really just go do the redesign. – John Saunders Mar 17 '12 at 04:47
  • @JohnSaunders: Even if I start with a fresh redesign, I don't know how to handle this. Is there an article or something I can read that deals with this problem directly, without me having to read and learn every tiny little detail about 80 different kinds of parsers? – mpen Mar 17 '12 at 04:51
  • A fresh design won't need to play games with the stack. – John Saunders Mar 17 '12 at 04:54
  • @JohnSaunders: I'm fairly certain most parsers will use a stack of some sort. I was fully aware even when posting the question that I wouldn't be able to access the C# call-stack directly; I was aiming more towards "how do I achieve this effect" or "create my own stack such that I can get the execution path that I desire". Your comments are less than helpful. – mpen Mar 17 '12 at 05:57
  • I meant "won't need to play games with the CLR runtime stack". In particular, it sounds like you meed some sort of "virtual machine", possibly a stack machine, but perhaps with bookmarks (be able to mark the current stack location and pop to that point later on). That's what I meant about a new design. Start over from your requirements (maybe take the time to restate the requirements thoroughly) then start designing again based on what you've learned, and _without any assumptions about how to implement_. – John Saunders Mar 17 '12 at 15:24

2 Answers2

2

The problem has nothing to do with the || operator. The problem is that you forgot to backtrack on a failed Parse. If Parse returns false, you need to restore i and any other state variables to their original value so the next parser can give it a try. C# is not a goal-seeking language. If you want backtracking you will have to do it yourself. (Or switch to a language with backtracking, like Prolog.)

Raymond Chen
  • 44,448
  • 11
  • 96
  • 135
  • I didn't forget; I know I need to backtrack, I just don't know *how* to backtrack. Restoring `i` is easy, it's the rest of the state that's tricky. – mpen Mar 17 '12 at 17:30
  • I spent 45 minutes learning how your program works and then debugging it. Once you restore `i` on a failed parse, the program works fine. It correctly stops parsing after character 10 because your definition of `literal` includes an open-brace. – Raymond Chen Mar 18 '12 at 15:48
  • Can you post your modifications? It shouldn't get past the first character, because the definition of `item` says to try a `kvp` first, and then it can't find the `:` and gives up, rather than trying to alternate option, a literal. And even if that part worked... I didn't think `literal` needed to exclude the opening brace, because the one before `e` should have been read in by `dict` already... – mpen Mar 18 '12 at 17:06
  • `dict` never got a chance because your `value` rule says to try `key` before `dict`, and `key` accepts a `literal`. All I did was update the `ref i` parameter only on a successful parse. – Raymond Chen Mar 18 '12 at 17:27
  • Aha...you're right! What do you mean by "only update `ref i` on a successful parse though? `StringTerm` and `RegexTerm` already did that..? – mpen Mar 18 '12 at 17:33
  • But you forgot `PlusTerm`, `OrTerm`, and `AndTerm`. And `OrTerm` needs to reset `i` before trying the `_b.Parse()`, otherwise `_b.Parse()` will try to resume where the failed `_a.Parse()` left off. – Raymond Chen Mar 18 '12 at 18:18
  • I didn't think I needed to reset it,because if `_a` fails it doesn't advance the pointer. But I guess `_a` can pass but it's child might fail, and then we'd be in a bad state. Didn't think about that. This solution still couldn't parse `(a|ab)c` for the input "abc" though... – mpen Mar 18 '12 at 18:49
  • Just stepping through the code would have identified these issues. – Raymond Chen Mar 18 '12 at 19:43
1

It strikes me as being fairly trivial- just rearrange the calls:

static void Main(string[] args)
{
    State = false;
    Console.WriteLine(Or());
    Console.ReadLine();
}

static bool Or()
{
    return A() && C() || AB() && C();
}

Or am I missing something? Maybe C() has side effects such that it should not be called twice?

EDIT: Now I understand what you are trying to do. Do yourself a favor. Listen to the suggestions you've gotten, get a copy of GPPG, or (probably much simpler) ANTLR, with ANTLRWorks. It is so, so much easier and less error prone than attempting to roll a parser by hand, and you still get C# at the end.

Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • `C()` is allowed to be called twice. It would have to be; we have to call it once to determine if we should backtrack and call `AB()`. It's trivial to do by hand here...but is there a programmatic way of re-arranging these function calls like this? – mpen Mar 17 '12 at 04:10
  • Programmatic given *what* as input and output? An expression tree? A chain of delegates? Some chained Tasks? I think that's the trouble you are running into with this question- you have some concrete use case in mind and are attempting to make it too abstract in asking the question. If you can provide a concrete, runnable program like the above that actually reproduces the problem you are encountering, rather than approximating it, then we can answer it. – Chris Shain Mar 17 '12 at 04:13
  • Fine, I'll post the full program and what I have so far, but when people post too much code people tend to get scared off. – mpen Mar 17 '12 at 04:15
  • I'm not necessarily asking for the full source- just a small truly representative example of the problem- tell me what I can and can't change. – Chris Shain Mar 17 '12 at 04:19
  • Updated question. You can change just about anything in the library, but I want to keep the grammar (in main) very simple, as I have it. Also, I want to be able to add new types of nodes easily; so we can make `Term` more complex, but the extensions should be pretty light. – mpen Mar 17 '12 at 04:20
  • I added an edit- basically I think (like everyone else thus far) that you'd be better off getting a parser generator. They will save you a huge amount of pain over trying to do it manually. – Chris Shain Mar 17 '12 at 04:38