Experiment – Projectional Viewer

Hacker News Top Tools

Summary

An experiment to elide implementation details from code in Emacs using a pipeline with OhmJS and RWR, aiming to improve code readability by focusing on structure.

No content available
Original Article
View Cached Full Text

Cached at: 09/11/26, 02:24 PM

# Experiment — Projectional Viewer Source: [https://programmingsimplicity.substack.com/p/experiment-projectional-viewer](https://programmingsimplicity.substack.com/p/experiment-projectional-viewer) Modern programming languages bury structure under implementation detail\. The signal\-to\-noise ratio is too low — there’s too much*how*cluttering up the*what*— and that makes it hard to reason about anything beyond fairly simple problems\. This experiment asks whether implementation detail can be elided from code, rather than removed\. The detail stays in the source; it’s just toggled out of view in the editor\. The goal is a clearer picture of a solution’s structure, without having to wade through the mechanics of how that structure gets executed\. The immediate irritant was PEG \(parsing expression grammar\) libraries\. I know grammars reasonably well, and most PEG implementations I’ve used smear the grammar itself under a layer of code for capturing and acting on matches during and after the parse\. The grammar — the actual structure of what’s being recognized — disappears into the plumbing\. A full round\-trip projectional editor, where you edit the elided view and it writes back to the source, is hard to build well\. So this experiment settles for a one\-way trip: elide the detail for reading, without trying to support editing the elided form\. The question is whether that lesser goal is easy to reach and still worth having\. Underlying this is a way of treating a “grammar” as a DSL for pattern matching — one little language embedded in another\. If eliding works here, it suggests a broader path: composing many small special\-purpose languages into a single codebase, instead of forcing every concern through one general\-purpose paradigm\. I started from a small, real example: a phone\-number grammar written in Rebol, implementation detail and all\. ``` parse "(416)555-1234" [ "(" copy area 3 digits ")" copy exch 3 digits "-" copy line 4 digits (print rejoin [area "-" exch "-" line]) ] ``` The`copy area`,`copy exch`,`copy line`, and the trailing`\(print rejoin \.\.\.\)`block are all implementation detail — they’re about capturing values and doing something with them, not about the shape of a valid phone number\. Stripped of that, the grammar itself is just: ``` parse "(416)555-1234" [ "(" 3 digits ")" 3 digits "-" 4 digits ] ``` To get from one to the other automatically, I had Claude Sonnet 5 Code write an Emacs Lisp file,`pelide\-view\.el`, that shells the buffer \(or region\) out to a small pipeline built on T2T: an OhmJS parser followed by RWR rewrite semantics\. The pipeline reads the annotated source and hands back the elided grammar, which the elisp code then displays back in the editor\. The grammar for the pipeline’s parser \(`pelide\.ohm`\) and its rewrite rules \(`pelide\.rwr`\) are in the appendix, as is the generated`pelide\-view\.el`\. The rest of the code lives in the[github repo](https://github.com/guitarvydas/rebol)\. Given the time available, I only ran this against the one example above\. Whether it generalizes to messier grammars is an open question\. To reproduce it: eval and load`pelide\-view\.el`, load`ex\.r`into Emacs, then hit`C\-c p t`to toggle into the elided view, and again to toggle back\. The pipeline did what it was supposed to: it elided the`copy`clauses and the trailing action block, and left behind the bare grammar shown above\. Toggling back and forth in Emacs worked as expected\. [![](https://substackcdn.com/image/fetch/$s_!hhmN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafebce7e-ce9b-4a0a-9245-0ec36a6c9219_486x340.png)](https://substackcdn.com/image/fetch/$s_!hhmN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fafebce7e-ce9b-4a0a-9245-0ec36a6c9219_486x340.png) [![](https://substackcdn.com/image/fetch/$s_!HfrX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b748079-bab5-4602-9d1c-8f722ab2d013_486x340.png)](https://substackcdn.com/image/fetch/$s_!HfrX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b748079-bab5-4602-9d1c-8f722ab2d013_486x340.png) Under the hood, this is my T2T transmogrifier technology at work: OhmJS does the parsing, and a custom DSL I call RWR does the filtering, rewriting, and eliding\. The result is handed back to Emacs for read\-only viewing\. Even from this one example, the elided grammar reads better than the annotated original — it’s easier to see, at a glance, what pattern is actually being matched\. That’s a good sign for the general idea\. It’s a small enough result that I’d want to try it against more grammars before drawing strong conclusions, but it seems worth pursuing\. A natural next step would be writing elision grammars for other, more current PEG implementations — Lua’s and Janet’s come to mind\. This also suggests that using T2T as a filter is fast and usable enough on modern hardware — the lag when toggling is almost imperceptible\. I don’t expect that to change as long as we keep the little languages little: there’s no need to write grammars for Turing\-complete behemoths here, since small code snippets are all that’s required\. The experiment also points at a follow\-on problem worth tackling: writing grammars that can detect the presence of elidable code within a larger file, rather than assuming the whole file is a single elidable unit\. This experiment is one piece of a larger question: how do you let many small DSLs coexist inside a single project, instead of forcing everything through one general\-purpose language? One direction already underway is transmogrification rather than elision: writing in a small meta\-language and generating standard target languages from it\. The PBP kernel is built this way — three source files \(`0d\.rt`,`stock\.rt`,`jit\.rt`\) written in the`\.rt`meta\-language, currently transmogrified into Python, JavaScript, and Common Lisp\. The transmogrifier itself works \(see the[kernel repo](https://github.com/guitarvydas/pbp/tree/main/kernel)for details\), but I haven’t yet run the projectional\-viewing experiment against it\. A further\-out direction is diagrammatic programming: generating code snippets from a diagrammatic programming language \(DPL\) rather than text\. There are small, early examples of this using drawio — a[decision\-tree repo](https://github.com/guitarvydas/dtree)and a[state\-machine repo](https://github.com/guitarvydas/sm-pbp)— but again, the elision experiment hasn’t been done against them, and it isn’t even clear yet how drawio would couple to Emacs\. ``` ;;; pellide-view.el --- Shell out to PELLIDE for a condensed view -*- ;;; lexical-binding: t; -*- (defgroup pellide-view nil "Shell buffer/region out to the PELLIDE tool for a condensed view." :group 'tools) (defcustom pellide-view-command "pellide" "Shell command to run. Reads source on stdin, writes condensed view on stdout." :type 'string) (defcustom pellide-view-command-args nil "Extra CLI args to pass to `pellide-view-command'." :type '(repeat string)) (defvar-local pellide-view--original nil "Buffer-local stash of the pre-toggle source, used by `pellide-view-toggle-in-place'.") (defun pellide-view--run (text) "Pipe TEXT through `pellide-view-command'; return its stdout as a string." (with-temp-buffer (insert text) (let ((exit-code (apply #'call-process-region (point-min) (point-max) pellide-view-command t t nil pellide-view-command-args))) (unless (zerop exit-code) (error "%s exited %s: %s" pellide-view-command exit-code (buffer-string))) (buffer-string)))) ;;;###autoload (defun pellide-view-region-or-buffer () "Shell the region (or whole buffer) out to PELLIDE; show the result read-only in a `*PELLIDE View*' side window. Non-destructive -- source buffer is untouched." (interactive) (let* ((text (if (use-region-p) (buffer-substring-no-properties (region-beginning) (region-end)) (buffer-string))) (mode major-mode) (result (pellide-view--run text)) (out-buf (get-buffer-create "*PELLIDE View*"))) (with-current-buffer out-buf (let ((inhibit-read-only t)) (erase-buffer) (insert result) (funcall mode) (setq buffer-read-only t))) (display-buffer out-buf '((display-buffer-reuse-window display-buffer-in-side-window) (side . right) (window-width . 0.5))))) ;;;###autoload (defun pellide-view-toggle-in-place () "Toggle the CURRENT buffer's text between source and PELLIDE's condensed view. Destructive: only safe if you don't edit while in the condensed view -- PELLIDE is not assumed invertible, so toggling back just restores the stashed original, discarding any edits made to the condensed text." (interactive) (if pellide-view--original (let ((orig pellide-view--original) (modified (buffer-modified-p))) (setq pellide-view--original nil) (setq buffer-read-only nil) (erase-buffer) (insert orig) (set-buffer-modified-p modified)) (let ((orig (buffer-string)) (modified (buffer-modified-p))) (erase-buffer) (insert (pellide-view--run orig)) (setq buffer-read-only t) (setq pellide-view--original orig) (set-buffer-modified-p modified)))) (global-set-key (kbd "C-c p v") #'pellide-view-region-or-buffer) (global-set-key (kbd "C-c p t") #'pellide-view-toggle-in-place) (setq pellide-view-command "~/projects/rebol/pellide") (provide 'pellide-view) ``` ``` pelide { main = "parse" spaces item spaces pGrammar spaces item = string | id string = "\"" (~"\"" any)+ "\"" id = letter idtail* idtail = alnum pGrammar = "[" grammarItem+ "]" grammarItem = | space -- space | string -- string | copyItem -- copy | topLevelActionItem -- action | ~"[" ~"]" ~"(" ~")" ~"copy" ~space ~string any -- other topLevelActionItem = "(" spaces actionItem+ spaces ")" copyItem = "copy" spaces id spaces int? spaces id actionItem = | "(" spaces actionItem+ spaces ")" -- recparen | "[" spaces actionItem+ spaces "]" -- recbrack | ~"]" ~"[" ~"(" ~")" ~"copy" any -- other int = digit+ } ``` ``` %rewrite pelide { main [_parse ws1 item ws2 pGrammar ws3] = ‛parse«ws1»«item»«ws2»«pGrammar»«ws3»’ item [x] = ‛«x»’ string [lq cs+ rq] = ‛«lq»«cs»«rq»’ id [letter idtail*] = ‛«letter»«idtail»’ idtail [alnum] = ‛«alnum»’ pGrammar [lb item+ rb] = ‛«lb»«item»«rb»’ grammarItem_space [s] =‛«s»’ grammarItem_string [s] =‛«s»’ grammarItem_copy [x] =‛«x»’ grammarItem_action [x] =‛«x»’ grammarItem_other [x] = ‛«x»’ topLevelActionItem [lp ws1 A+ ws2 rp] = ‛’ copyItem [_copy ws1 id ws2 int? ws3 id2] = ‛«int»«ws3»«id2»’ actionItem_recparen [lp ws1 a+ ws2 rp] =‛’ actionItem_recbrack [lb ws2 a+ ws3 rb] =‛’ actionItem_other [x] =‛’ int [digit+] = ‛«digit»’ } ``` *Email*:[ptcomputingsimplicity@gmail\.com](mailto:[email protected]) *Substack*:[paultarvydas\.s\. bstack\.com](http://paultarvydas.substack.com/) *Videos*:[https://www\. youtube\.com/@programmingsimplicity2980](https://www.youtube.com/@programmingsimplicity2980) *Discord*:[https://discord\.gg/65YZUh6J\. q](https://discord.gg/65YZUh6Jpq) *Leanpub*:[https:\. /leanpub\.com/u/paul\-tarvydas](https://leanpub.com/u/paul-tarvydas) *Twitter*: @paul\_tarvydas *Bluesky:*@paultarvydas\.bsky\.social *Mastodon:*@paultarvydas *\(earlier\) Blog:*[guitarvydas\.github\.io](http://guitarvydas.github.io/) *References:*[https://guitarvydas\.github\.io/2024/01/06/References\.html](https://guitarvydas.github.io/2024/01/06/References.html) *Notes and Back of the Napkin Scribbles*:[https://github\.com/guitarvydas/napkin\-notes](https://github.com/guitarvydas/napkin-notes%5D) *Paid subscriptions are a voluntary way to support this work\.* [Share](https://programmingsimplicity.substack.com/p/experiment-projectional-viewer?utm_source=substack&utm_medium=email&utm_content=share&action=share) [Leave a comment](https://programmingsimplicity.substack.com/p/experiment-projectional-viewer/comments) #### Discussion about this post ### Ready for more?

Similar Articles

A Vompeccc Case Study: Spotify as Pure ICR in Emacs

Hacker News Top

A deep-dive case study showing how the author built a Spotify client inside Emacs using the VOMPECCC completion framework, demonstrating modular integration of Consult, Marginalia, and Embark packages.

Offline JS Playground

Product Hunt

An offline JavaScript playground for writing, running, and testing snippets directly in Chrome, ideal for quick experiments and debugging.