A sidescrolling game as a Pratt parser
1. NUD
is my movement towards a Pratt parser.
Tbh, I was having a lot of difficulty with where-to-put the lisp code parsed from new-grammar-language.
So I wrote a silly program first, themed around moving a cursor around inside lists via cdr-coding.
Well, at least I could actually write this. Next, I'm going to use the silly physical-intuition cdr-coded cons manipulation at a cursor to insert the results of the Pratt parser I'm working on implementing into an existing lisp cons.
There are a bunch of interesting properties of my cons-traverser already, I think, though I'm not an expert on this topic.
1.1. Anyway, let's check it out.
1.1.1. Example
CL-USER> (in-package :nud/user) #<PACKAGE "NUD/USER"> NUD/USER> '(1 2 (3 4 (5 6)) 7) (1 2 (3 4 (5 6)) 7) NUD/USER> (gird *) ; in: GIRD * ; (NUD/USER::GIRD *) ; ; caught STYLE-WARNING: ; undefined function: NUD/USER::GIRD ; ; compilation unit finished ; Undefined function: ; GIRD ; caught 1 STYLE-WARNING condition ; Evaluation aborted on #<UNDEFINED-FUNCTION GIRD {1002C73DE3}>. NUD/USER> (clad *) ; oops it was named clad not gird (NUD/MAIN::START #:CURSOR) (NUD/MAIN::START #:CURSOR (1 2 (3 4 (5 6)) 7)) #1=(NUD/MAIN::START #:CURSOR (1 2 (3 4 (5 6)) 7) . #1#) (> . #1=(#:CURSOR (1 2 (3 4 (5 6)) 7) NUD/MAIN::START . #1#)) (1 2 (3 4 (5 6)) 7) NUD/USER> ; so, clad creates a circular outside list with an uninterned cursor. ; No value NUD/USER> ^^ ; go into the list after the cursor (focus of second return) (> #:CURSOR 2 (3 4 (5 6)) 7) 2 NUD/USER> ; Note we "swapped" with the first element of the list. That's how I chose it to work. ; No value NUD/USER> >> (> #:CURSOR (3 4 (5 6)) 7) (3 4 (5 6)) NUD/USER> >> (> #:CURSOR 7) 7 NUD/USER> >> (> #:CURSOR) NIL NUD/USER> >> (> . #1=(#:CURSOR (1 2 (3 4 (5 6)) 7) NUD/MAIN::START . #1#)) (1 2 (3 4 (5 6)) 7) NUD/USER> ; back to the start ; No value NUD/USER> ^^ (> #:CURSOR 2 (3 4 (5 6)) 7) 2 NUD/USER> >> (> #:CURSOR (3 4 (5 6)) 7) (3 4 (5 6)) NUD/USER> ^^ (> #:CURSOR 4 (5 6)) 4 NUD/USER> >> (> #:CURSOR (5 6)) (5 6) NUD/USER> ^^ (> #:CURSOR 6) 6 NUD/USER> >> (> #:CURSOR) NIL NUD/USER> >> (> #:CURSOR (5 6)) (5 6) NUD/USER> ; When we run off the end of the list, we actually swap the initial element back into the list. ; No value NUD/USER> >> (> #:CURSOR) NIL NUD/USER> >> (> #:CURSOR (3 4 (5 6)) 7) (3 4 (5 6)) NUD/USER> ; Oh, right, it was nested, wasn't it. ; No value NUD/USER> >> (> #:CURSOR 7) 7 NUD/USER> >> (> #:CURSOR) NIL NUD/USER> >> (> . #1=(#:CURSOR (1 2 (3 4 (5 6)) 7) NUD/MAIN::START . #1#)) (1 2 (3 4 (5 6)) 7) NUD/USER>
1.1.2. Choices
So, I had to consider that cdr-coding basically lets you destructively mess up the cons (list, tree) you're working with. It does not seem possible to easily undo these destructive operations. The lisp term for destructive here is "nonconsing" which is denoted by the letter n at the start of function names, like nreverse - nreverse doesn't make any new conses (automatically garbage collected lisp memory allocations), but this means it used the conses already consed for its input to build its output, so any connections to its inputs conses now point inside nreverse's output somewhere.
Since there is no obvious way to reverse destruction, or to dry-run destruction, I elected
not to give much control to the nud/user user. Instead they just get two operations:
=>>=
and =^^=
.
1.2. Game Feels
I am quite happy with this gamification of player-driven traversals of a cons. It evokes feelings of sidescrollers that can only move forward; and there's a sense of beginning and finishing a (game) traversal, when you start in and drop back into the outer circular list 'start screen' location.
It clearly shows how the cons is a tree of singly linked lists, with the rule-breaking witchcraft of swapping-back-out of a subtree when you reach the end. Since you can either go past, or climb into nested lists, these seem like doors in classic side-scroller games to me: And when you have traversed the room, you just exit the door to where you started.
1.3. Grammar parser feels
The fact that there is no turning-around-and-going-back feature, just finishing the level, and restarting the level if you wanted to go in a certain door and accidentally pushed the screen past it in your last try connects with host language (i.e. lisp code generation) from parsing a tenant language grammar. The unavailability of the past without finishing this traversal, and starting a new pass (from the beginning).
This acts to prevent explodingly complicated infinite-random-access-style processing, while acknowledging that a two-pass approach as a strategy; and that it is really literally two passes through a singly linked list.
The rule-breaking fact that any list entered by ^^ can be traversed any number of times as a consequence of the same rule being used to enter and exit the original cons at all, I guess softens this. Inside lists can be the subject of multiple passes, even if the outer list is ultimately only traversed with a single pass.
1.4. Next
Of course, there is no way of making changes currently. I guess there will be
- push in front of me from register
- pop in front of me to register
- push inside list in front of me from register
- pop from list in front of me to register
as well as something like
- execute symbol
that is aware of the left and right binding precedence of the symbol (whether the NUD or LED of the symbol is used / receives an input or returns an input). I'm still shaky on that.
1.4.1. Games
I am excited to make a text-based side-scroller game in this format. I think that performing passes to collect artifacts / remove hazards / add features to the side-scrolling world are perfectly exciting though slightly mindbending and cdr-coding oriented games.
1.4.2. Grammar parser
The implication is that the original list being traversed would be the tokens of the language whose grammar is being parsed.
- Psychically,
NUD/USER> '( 2 + 3 * 4 + 5 ) (2 + 3 * 4 + 5) NUD/USER> (clad *) (NUD/MAIN::START #:CURSOR) (NUD/MAIN::START #:CURSOR (2 + 3 * 4 + 5)) #1=(NUD/MAIN::START #:CURSOR (2 + 3 * 4 + 5) . #1#) (> . #1=(#:CURSOR (2 + 3 * 4 + 5) NUD/MAIN::START . #1#)) (2 + 3 * 4 + 5) NUD/USER> ^^ (> #:CURSOR + 3 * 4 + 5) + NUD/USER> >> (> #:CURSOR 3 * 4 + 5) 3 NUD/USER>
We can imagine here "inserting"
(+ 2)
before continuing
(> #:CURSOR 3 * 4 + 5) 3 NUD/USER> >> (> #:CURSOR * 4 + 5) * NUD/USER> >> (> #:CURSOR 4 + 5) 4 NUD/USER> >> (> #:CURSOR + 5) +
and "inserting"
((* 3 4))
and continuing:
(> #:CURSOR + 5) + NUD/USER> >> (> #:CURSOR 5) 5 NUD/USER> >> (> #:CURSOR) NIL
and "inserting"
((+ 5))
Now, on those running nconc, in a second pass:
(apply 'nconc '((+ 2) ((* 3 4)) ((+ 5))))
I.e.
(eval form)
Files
Get lispmoo2
lispmoo2
Status | Prototype |
Author | screwtape |
Tags | common-lisp, emacs, lisp, mcclim, moo, new-kind-of-society, swank, Text based |
More posts
- Add Another System Definition to Arrokoth PR4 days ago
- NUD GOLF graphics choices and more on the live show in a mo'5 days ago
- NUD GOLF 2 more nconc6 days ago
- NUD GOLF7 days ago
- LISPMOO2 INSTRUCTIONS (IMPORTANT)67 days ago
- repeatedly-eval-qt - good old-fashioned AI, in my lispmoo2?70 days ago
- Richard Waters' Series and lispmoo271 days ago
- Itches Of Mine Lispmoo2 Scratches72 days ago
- Princess revisited72 days ago
Comments
Log in with itch.io to leave a comment.
It occurs to me there's no link to the git.
https://codeberg.org/tfw/nud
Well, it's not perfect and for some reason my :export both didn't work but happy 2025 everyone. Chatter at me on the 'stodon plz. Remember the live show is every Wednesday 0UTC (Tuesday night in Americas various). We're mostly doing interviews when we can now. Check the pinned Mastodon toots for recent interviews or the peertube https://communitymedia.video/c/screwtape_channel/videos:
https://mastodon.sdf.org/@screwtape/