The Robot World Repo on GitHub
The Forth Repo on GitHub

#ElonaldDelendaEst! I test some CASE results. Looks good!

I wrote two tests, one without a default and one with, referring to the case statement we’ve been using in the compiler tests already:

    def test_famous_case_behavior(self):
        f = Forth()
        case = ': T CASE 1 OF 111 ENDOF 2 OF 222 ENDOF ENDCASE ;'
        f.compile(case)
        assert f.stack.is_empty()
        f.compile(' 1 T')
        assert f.stack.pop() == 111
        assert f.stack.is_empty()
        f.compile(' 2 T')
        assert f.stack.pop() == 222
        assert f.stack.is_empty()
        f.compile(' 3 T')
        assert f.stack.is_empty()

    def test_famous_case_behavior_default(self):
        f = Forth()
        case = ': T CASE 1 OF 111 ENDOF 2 OF 222 ENDOF >R 666 R> ENDCASE ;'
        f.compile(case)
        assert f.stack.is_empty()
        f.compile(' 1 T')
        assert f.stack.pop() == 111
        assert f.stack.is_empty()
        f.compile(' 2 T')
        assert f.stack.pop() == 222
        assert f.stack.is_empty()
        f.compile(' 3 T')
        assert f.stack.pop() == 666

They both run green. The second one’s default code is worth mentioning:

>R 666 R>

The CASE has two characteristics that make the default tricky.

First, when the case exits, the stack will contain only whatever the individual OF-ENDOF or default put on it. The input value will be consumed. The OF statements consume the input value if that OF is selected, before the user’s OF code is executed. (We compile OVER = 0BR out DROP … to accomplish that.)

So before each OF, the input value is on the stack. Therefore, upon entering the default, the input value will be on the stack.

Second, when the CASE-ENDCASE exists, the stack will contain only whatever values the code inside has put there: the input value is gone. Therefore, if the CASE drops through, there is a final DROP that is executed to get rid of the input value.

Therefore, if you DO have default code, you have to leave the input value (or any useless value, I suppose) on top of the stack, to be dropped. So the example I use here stashes the input, computes the 666 answer, and unstashes the input. I found this example on the fig-Forth site, I think.

So CASE is working as intended. Commit: two new tests verify CASE operation.

Summary

Just the tests. There’s code to be improved. Probably tomorrow for that. I just had a few moments at the keyboard.

I think CASE is working as intended and it’s the most complex conditional structure we’ll probably build. We’ll want to bring the other conditional and looping words in line with th is implementation. the CompileInfo object and stack protocol is pretty clean and I think we can make it better.

I think there is some rearrangement needed between Lexicon, Word, and Forth objects, that will come out of reviewing the CASE code and adjusting the other words. We’ll wind up with shorter and more clear word definitions.

We need to mark these words as working only in compilation state. Their behavior outside compilation state is undefined and they will not work there. My Forth guides tell me that marking them compilation only is the thing.

I’m pleased with CASE: It has proceeded nicely, with tests that checked the compiled code, and tests that check the actual operation. Yummy

#ElonaldDelendaEst! See you next time!