This went fine for a bit, but I quickly encountered overly complicated situations. Without access to the Sprache parser implementations, I was left parsing string values to extract expressions used within statements. This was less than ideal, so I pivoted and merged the separate projects back into a single one. Almost immediately the job became much simpler.
Of course, these adventures never proceed without at least one critical issue. Just when I thought I was done, I pointed my glorious new line/statement parser at adventure.bas. It blew up on an input that I thought I had handled but slipped through the cracks, an expression like
(X AND Y)=Z. I put in a quick fix but it was not sufficient to handle the general situation. Basically, due to the way that I implemented AND/OR expressions — having them in the upper level with separate forks for string and numeric expressions — I kept running into examples beyond the reach of my parser. Seeing that I was on the verge of a major refactor, I added several new tests. Then I set out to eliminate the string/number parser dichotomy and instead lean on semantic analysis to ensure valid results.
I started small by just adding the type information. With the groundwork laid, I could then add operator type checking. From there, it wasn’t a big leap to finalize the simplified grammar with proper type checking.
With the path now fully unblocked, I could complete the final set of statements needed to parse
adventure.bas! A finishing touch to add the visitor pattern, similar to what I had done for expressions, and it was complete.
The code can now produce a valid AST of a sort for some subset of GW-BASIC programs.
Here is an example program, followed by a textual output of how my GWParse library interprets it:
10 CLS 20 INPUT "WHAT IS YOUR NAME";A$ 30 IF A$="" THEN PRINT "TRY AGAIN." : GOTO 20 40 PRINT "HELLO, ";A$;"!" Line(10, Cls()) Line(20, Input("WHAT IS YOUR NAME", StrV(A))) Line(30, If(Eq(StrV(A), StrL("")), Print(StrL("TRY AGAIN."))), Goto(20)) Line(40, Print(StrL("HELLO, "), StrV(A), StrL("!")))
Next time, we will see if I can finally complete the trifecta — C# code generation.