parinfer / parinfer.js

Let's simplify the way we write Lisp
https://shaunlebron.github.io/parinfer
MIT License
1.75k stars 40 forks source link

How to support other Lisp dialects #209

Open shaunlebron opened 1 year ago

shaunlebron commented 1 year ago

Parinfer is a lazy, partial reader— allowing it the opportunity to ignore most irrelevant differences between dialects, but it needs to support more specific dialect features

Parinfer assumes Clojure-like syntax— blowing past characters presumed to have no effect on parens and indentation. This allows some flexibility with other dialects, but eventually causes problems with other language features below:

See issues:

Some dialects we look at:

Here-strings

strings delimited by custom phrases

Multi-line strings

Nestable block comments

may be nested, unlike in C /*.../*...*/...*/)

Bracket types

Comment lines

Comment forms

A preceding # can re-interpret the usual ; comment as commenting the next form instead of to the end of line:

Literal symbols

pipes |...| allow special characters inside symbols. Sometimes \| is allowed inside (CL, not Racket):

shaunlebron commented 1 year ago

Rough plan:

1. Put some separation between Reader and Parinfer

For example, I built a minimal auto-formatting tool for Clojure as a set of callbacks to Parinfer’s reader here:

https://github.com/parinfer/parindent/blob/gh-pages/read.js

This reader takes a hooks object of callbacks— each triggered at certain times when reading:

This might help me think more generally about what Parinfer needs to do and when.

2. Allow featureFlags or dialect option

If it is tractable, all featureFlags may be built into one reader, with combination presets supported with a dialect option.

3. Consider more non-recoverable “inside-out” error states

These feature flags may create more possibilities for unintended changes to strings or comments. (e.g. ERROR_QUOTE_DANGER)

The crux of the issue I believe is how non-directional delimiters like " (and now |) can be reversed while the document is being edited— i.e. a character can suddenly switch from meaning “open” to “close” if one is typed behind it. And rather than it becoming imbalanced (thus suspending Parinfer), the newly opened delimiter can become closed by another character— accidentally entering into a valid but unintended “inside-out” state, erroneously allowing Parinfer to run and make changes to non-structural parts of the code.

shaunlebron commented 1 year ago

We can use onecompiler.com to test Racket and Common Lisp:

https://onecompiler.com/racket/3xjvghuay https://onecompiler.com/commonlisp/3xjvgwzbj