v0.2: Pico-8 Lua dialect grammar and language
Reorganize into grammars/<name>/ subdirs ( Zed's [grammars.*] supports a `path` field, so both grammars ship from this repo without a sibling- repo split ). Vendor tree-sitter-lua as the fork base for tree-sitter- pico8-lua; upstream MIT license preserved at grammars/pico-8-lua/ UPSTREAM-LICENSE.md. Dialect features added: != as ~= alias, \ integer divide, ^^ binary xor, >>> / <<> / >>< shifts and rotates, compound-assignment statements, memory peek prefixes @ % $ (% coexists with binary modulo), single-line `if (cond) stmt [else stmt]` and `while (cond) stmt`, statement-level print shorthand ?, and `#include path` directives. Identifier rule no longer accepts ! ? @ $ ( upstream did ). Pico-8 Lua language ( languages/pico-8-lua/, suffix .p8lua ) ships highlights with the full ~110 PICO-8 builtins as @function.builtin. The cart injection now hands __lua__ bodies to pico-8-lua, so .p8 carts and bare .p8lua files share the dialect-aware grammar. Examples updated to exercise the dialect end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,146 +1,186 @@
|
||||
# zed-p8
|
||||
|
||||
A Zed extension for the [PICO-8](https://www.lexaloffle.com/pico-8.php) fantasy
|
||||
console. The goal is reasonable editor support for the entire `.p8` cartridge
|
||||
file format and for PICO-8's Lua dialect — even where PICO-8 deviates from
|
||||
console. Reasonable editor support for the entire `.p8` cartridge file format
|
||||
and for PICO-8's Lua dialect — including the parts where PICO-8 deviates from
|
||||
standard Lua 5.2 (compound assignments, `?` print shorthand, single-line
|
||||
`if (cond) ...`, `!=`, binary literals, peek operators, and so on).
|
||||
`if (cond) ...`, `!=`, `0b...` binary literals, integer divide `\`, peek
|
||||
operators `@`/`%`/`$`, the rotate/logical-shift family `<<>` / `>><` / `>>>`,
|
||||
and `#include`).
|
||||
|
||||
## Status — v0.1 scaffold
|
||||
## Status — v0.2
|
||||
|
||||
Working today:
|
||||
|
||||
- A small tree-sitter grammar (`p8_cart`, in this repo's root) that parses
|
||||
the `.p8` cartridge container: the magic header line, `version` line, and
|
||||
the named sections `__lua__`, `__gfx__`, `__gff__`, `__label__`, `__map__`,
|
||||
`__sfx__`, `__music__`. Unknown `__name__` markers are accepted as a
|
||||
fallback `unknown_section`.
|
||||
- A Zed language definition `Pico-8 Cartridge` (file suffix `.p8`) that uses
|
||||
this grammar for outline, section-marker highlighting, and an outline view.
|
||||
- An injection that hands the body of the `__lua__` section to Zed's
|
||||
built-in Lua language for syntax highlighting.
|
||||
- **`tree-sitter-p8-cart`** ( `grammars/p8-cart/` ) — parses the `.p8`
|
||||
cartridge container: the magic header line, `version` line, and the named
|
||||
sections `__lua__`, `__gfx__`, `__gff__`, `__label__`, `__map__`, `__sfx__`,
|
||||
`__music__`. Unknown `__name__` markers fall through to `unknown_section`.
|
||||
- **`tree-sitter-pico8-lua`** ( `grammars/pico-8-lua/` ) — fork of
|
||||
[`tree-sitter-grammars/tree-sitter-lua`](https://github.com/tree-sitter-grammars/tree-sitter-lua)
|
||||
with the PICO-8 dialect added. Handles every dialect form the manual
|
||||
documents: see *Dialect coverage* below. Upstream attribution is preserved
|
||||
in `grammars/pico-8-lua/UPSTREAM-LICENSE.md`.
|
||||
- **`Pico-8 Cartridge`** language ( `languages/pico-8-cart/`, suffix `.p8` ) —
|
||||
config, marker highlights, outline view, and an injection that hands the
|
||||
`__lua__` body to the Pico-8 Lua grammar.
|
||||
- **`Pico-8 Lua`** language ( `languages/pico-8-lua/`, suffix `.p8lua` ) —
|
||||
config, dialect-aware highlights with PICO-8 builtins recognized, brackets,
|
||||
indents, outline.
|
||||
|
||||
Known limitations:
|
||||
The `.p8lua` suffix is the convention for bare Pico-8 Lua source files — the
|
||||
ones pulled into a cart via `#include`. Plain `.lua` files are intentionally
|
||||
*not* claimed by this extension, so users who keep stock Lua files alongside
|
||||
their PICO-8 work continue to get standard Lua treatment.
|
||||
|
||||
### Dialect coverage
|
||||
|
||||
| Feature | Status |
|
||||
|---|---|
|
||||
| `!=` ( alias for `~=` ) | ✓ |
|
||||
| Compound assignment: `+= -= *= /= %= \= ^= ..= &= \|= ^^= <<= >>= >>>= <<>= >><=` | ✓ |
|
||||
| Integer divide `\` and modulo `%` ( binary ) | ✓ |
|
||||
| Bitwise XOR `^^` ( binary, in addition to upstream's `~` ) | ✓ |
|
||||
| Logical shift right `>>>`, rotate left `<<>`, rotate right `>><` | ✓ |
|
||||
| Hex literals with fractional part: `0x11.4000` | ✓ |
|
||||
| Binary literals: `0b1010` | ✓ |
|
||||
| Memory peek prefix unary: `@addr`, `%addr`, `$addr` | ✓ |
|
||||
| Single-line `if (cond) stmt [else stmt]` ( no `then`/`end` ) | ✓ |
|
||||
| Single-line `while (cond) stmt` ( no `do`/`end` ) | ✓ |
|
||||
| `?` print shorthand statement | ✓ |
|
||||
| `#include path` directive | ✓ |
|
||||
| `_init` / `_update` / `_update60` / `_draw` highlighted as builtins | ✓ |
|
||||
|
||||
### Known limitations
|
||||
|
||||
- **PICO-8 Lua dialect is not fully parsed.** The injected grammar is plain
|
||||
Lua 5.2, which does not understand `?` (print shorthand), `+=` and friends,
|
||||
`!=`, `0b...` literals, the `\` integer-divide operator, the `@`/`%`/`$`
|
||||
peek prefixes, or the single-line `if (cond) stmt` / `while (cond) stmt`
|
||||
forms. Code that uses any of those will show parse-error highlighting in
|
||||
those regions only — surrounding code remains correctly highlighted. See
|
||||
Roadmap below.
|
||||
- **No language server.** No completion, hover docs, or diagnostics for
|
||||
PICO-8 builtins yet. See Roadmap.
|
||||
- **No `.p8.png` support.** Only the plain-text `.p8` format is handled.
|
||||
PICO-8 builtins yet — only a static `function.builtin` highlight on
|
||||
recognized names. See Roadmap.
|
||||
- **No `.p8.png` support.** Only the plain-text `.p8` format is handled —
|
||||
the PNG-steganography variant is out of scope for a text-focused IDE
|
||||
extension.
|
||||
- **Hex sections are unhighlighted blobs.** `__gfx__`, `__map__`, `__sfx__`,
|
||||
`__gff__`, `__music__`, `__label__` parse as opaque line bodies. Roadmap
|
||||
v0.4 covers per-section highlighters.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
zed-p8/
|
||||
extension.toml ← Zed extension manifest
|
||||
grammar.js ← tree-sitter-p8-cart grammar source
|
||||
src/ ← generated parser ( committed; regenerate after grammar.js edits )
|
||||
package.json ← tree-sitter-cli devDependency
|
||||
tree-sitter.json ← tree-sitter-cli config ( auto-managed )
|
||||
extension.toml ← Zed extension manifest
|
||||
package.json ← workspace root; hosts tree-sitter-cli
|
||||
grammars/
|
||||
p8-cart/ ← cart-format tree-sitter grammar
|
||||
grammar.js
|
||||
tree-sitter.json
|
||||
src/ ← generated parser ( committed )
|
||||
pico-8-lua/ ← Pico-8 Lua dialect tree-sitter grammar
|
||||
grammar.js
|
||||
tree-sitter.json
|
||||
package.json ← marks this dir as ESM for node
|
||||
src/ ← generated parser + scanner.c ( committed )
|
||||
UPSTREAM-LICENSE.md ← MIT, tree-sitter-lua by Munif Tanjim
|
||||
languages/
|
||||
pico-8-cart/ ← Pico-8 Cartridge language files
|
||||
pico-8-cart/ ← Pico-8 Cartridge language files
|
||||
config.toml
|
||||
highlights.scm
|
||||
injections.scm ← injects pico-8-lua into __lua__ body
|
||||
outline.scm
|
||||
pico-8-lua/ ← Pico-8 Lua language files
|
||||
config.toml
|
||||
highlights.scm
|
||||
brackets.scm
|
||||
indents.scm
|
||||
injections.scm
|
||||
outline.scm
|
||||
examples/
|
||||
hello.p8 ← minimal test cart
|
||||
references/ ← upstream PICO-8 manual + Zed docs links
|
||||
hello.p8 ← minimal test cart
|
||||
references/ ← upstream PICO-8 manual + Zed doc links
|
||||
```
|
||||
|
||||
The cart grammar lives at the repo root rather than as a separate sibling
|
||||
repository. This keeps everything in one place during early development; if
|
||||
the grammar grows or wants to be reused outside Zed it can be split out
|
||||
later — the only file that needs to move with it is `grammar.js` plus the
|
||||
generated `src/`, and the `[grammars.p8_cart]` URL in `extension.toml` would
|
||||
need updating.
|
||||
Both grammars live in subdirectories of this same repository. Zed's
|
||||
`[grammars.*]` block supports a `path` field, so the extension manifest
|
||||
points each grammar at this repo's git URL plus the relevant subdir.
|
||||
|
||||
## Local development
|
||||
|
||||
Prerequisites: Node.js (for `tree-sitter-cli`) and Zed. Rust is NOT required
|
||||
unless/until we add a language-server harness.
|
||||
Prerequisites: Node.js ( for `tree-sitter-cli` ) and Zed. Rust is *not*
|
||||
required unless / until we add a language-server harness ( v0.3 ).
|
||||
|
||||
### Edit-and-reload loop
|
||||
```sh
|
||||
npm install # one-time, installs tree-sitter-cli
|
||||
```
|
||||
|
||||
1. Edit `grammar.js`.
|
||||
2. Regenerate the parser:
|
||||
### Edit a grammar and reload
|
||||
|
||||
1. Edit `grammars/<name>/grammar.js`.
|
||||
2. Regenerate from the grammar's directory:
|
||||
|
||||
```sh
|
||||
npx tree-sitter generate
|
||||
( cd grammars/p8-cart && npx tree-sitter generate )
|
||||
# or
|
||||
( cd grammars/pico-8-lua && npx tree-sitter generate )
|
||||
```
|
||||
|
||||
3. Sanity-check on a real cart:
|
||||
3. Sanity-check by parsing a sample file:
|
||||
|
||||
```sh
|
||||
npx tree-sitter parse examples/hello.p8
|
||||
( cd grammars/p8-cart && npx tree-sitter parse ../../examples/hello.p8 )
|
||||
( cd grammars/pico-8-lua && npx tree-sitter parse path/to/file.p8lua )
|
||||
```
|
||||
|
||||
4. Commit. The `[grammars.p8_cart]` block in `extension.toml` references this
|
||||
repo by `file://` URL and pins a commit SHA — Zed clones the grammar
|
||||
from that pinned revision, so changes only take effect after they're
|
||||
committed.
|
||||
4. Commit the regenerated `src/parser.c` along with the grammar change.
|
||||
Zed clones the grammar repo at the SHA in `extension.toml`, so changes
|
||||
only take effect after a commit.
|
||||
|
||||
5. Update `extension.toml`'s `rev` field to the new SHA, then in Zed run
|
||||
`zed: install dev extension` (or click *Install Dev Extension* on the
|
||||
Extensions page) and select this directory. Reinstall after every commit
|
||||
that should be picked up.
|
||||
5. Update the `rev` field of the affected `[grammars.*]` block(s) in
|
||||
`extension.toml` to the new SHA, then in Zed run `zed: install dev
|
||||
extension` ( or *Install Dev Extension* on the Extensions page ) and
|
||||
select this directory. Reinstall after every commit that should be
|
||||
picked up.
|
||||
|
||||
Logs: `zed: open log`. Run `zed --foreground` for live stdout.
|
||||
|
||||
### Editing language queries
|
||||
### Edit only language queries
|
||||
|
||||
Files under `languages/pico-8-cart/` (`highlights.scm`, `injections.scm`,
|
||||
`outline.scm`) are loaded directly by Zed — no regeneration needed. Reinstall
|
||||
the dev extension to pick up changes.
|
||||
Files under `languages/*/` ( `highlights.scm`, `injections.scm`, etc. )
|
||||
are loaded directly by Zed — no regeneration step needed. Reinstall the
|
||||
dev extension to pick up changes.
|
||||
|
||||
### Tests
|
||||
|
||||
Sample carts live under `examples/`. Use `tree-sitter parse` directly for
|
||||
verification — corpus-style `tree-sitter test` is not currently set up
|
||||
because the cart grammar's strict `extras: []` doesn't tolerate the
|
||||
leading newline that the test harness prepends to each fixture.
|
||||
|
||||
## Roadmap
|
||||
|
||||
### v0.2 — PICO-8 Lua dialect grammar
|
||||
|
||||
Fork [`tree-sitter-grammars/tree-sitter-lua`](https://github.com/tree-sitter-grammars/tree-sitter-lua)
|
||||
into `tree-sitter-pico8` and add the dialect extensions documented in the
|
||||
PICO-8 manual:
|
||||
|
||||
- Compound-assignment operators: `+= -= *= /= \= %= ^= ..= &= |= ^^= <<= >>= >>>= <<>= >><=`
|
||||
- `!=` as alias for `~=`
|
||||
- `\` (integer divide) and the rotate / logical-shift operators `<<>` `>><` `>>>`
|
||||
- Binary literals `0b...` and hex fractional literals `0x1.4p0` style
|
||||
- Single-line `if (cond) stmt` and `while (cond) stmt` ( no `then`/`do`/`end` )
|
||||
- `?` as a statement-level shorthand for `print`
|
||||
- The peek-prefix unary operators `@addr` `%addr` `$addr`
|
||||
|
||||
Then add a second language `Pico-8 Lua` here (separate from Zed's built-in
|
||||
`Lua`) and switch `injections.scm` to inject `pico-8-lua` instead of `lua`.
|
||||
|
||||
### v0.3 — Language server integration
|
||||
|
||||
Wire up [`japhib/pico8-ls`](https://github.com/japhib/pico8-ls) (or whichever
|
||||
PICO-8 LSP is most maintained at the time) for:
|
||||
Wire up [`japhib/pico8-ls`](https://github.com/japhib/pico8-ls) ( or whichever
|
||||
PICO-8 LSP is most maintained at the time ) for:
|
||||
|
||||
- Completion of PICO-8 builtins (`spr`, `circfill`, `btn`, `flr`, …)
|
||||
- Signature help and hover docs sourced from the manual
|
||||
- Cart-aware analysis ( the LSP already understands `.p8` section markers
|
||||
and only analyzes the `__lua__` body )
|
||||
- Per-cart diagnostics
|
||||
- Completion of PICO-8 builtins ( `spr`, `circfill`, `btn`, `flr`, … ).
|
||||
- Signature help and hover docs sourced from the manual.
|
||||
- Cart-aware analysis — the LSP already understands `.p8` section markers
|
||||
and only analyzes the `__lua__` body.
|
||||
- Per-cart diagnostics.
|
||||
|
||||
This will require a Rust component ( the `zed_extension_api` crate ) to
|
||||
download the language-server binary and define
|
||||
`language_server_command` — see [Zed's developing-extensions docs](https://zed.dev/docs/extensions/developing-extensions).
|
||||
This requires a Rust component ( the `zed_extension_api` crate ) that
|
||||
downloads the LSP binary and defines `language_server_command`.
|
||||
See [Zed's developing-extensions docs](https://zed.dev/docs/extensions/developing-extensions).
|
||||
|
||||
### v0.4 — Polish
|
||||
|
||||
- LuaCATS / EmmyLua stub file enumerating PICO-8's ~110 globals, for users
|
||||
who'd rather wire up `lua-language-server` against their `#include`-d
|
||||
`.lua` files.
|
||||
- Highlight rules for hex sections (`__gfx__`, `__map__`, `__sfx__`, etc.)
|
||||
so palette indices and note pitches show up distinctly.
|
||||
- Snippets for common idioms (`for x=0,127 do ... end`, the `_init`/`_update`/
|
||||
`_draw` triad).
|
||||
`.p8lua` files.
|
||||
- Per-section highlighters for the hex blocks: `__gfx__` colored by palette
|
||||
index, `__sfx__` / `__music__` parsed as note streams, `__map__` as tile
|
||||
indices, `__gff__` as flag bytes.
|
||||
- Snippets for common idioms ( the `_init` / `_update` / `_draw` triad,
|
||||
`for x=0,127 do … end`, palette swap setup, etc. ).
|
||||
|
||||
## References
|
||||
|
||||
@@ -148,7 +188,12 @@ download the language-server binary and define
|
||||
- Zed extension docs: see links in `references/zed-doc-links.md`
|
||||
- Cart file-format spec ( community wiki, not in the official manual ):
|
||||
https://pico-8.fandom.com/wiki/P8FileFormat
|
||||
- Upstream Lua grammar: https://github.com/tree-sitter-grammars/tree-sitter-lua
|
||||
( MIT, by Munif Tanjim — preserved in `grammars/pico-8-lua/UPSTREAM-LICENSE.md` )
|
||||
|
||||
## License
|
||||
|
||||
0BSD — see `LICENSE`.
|
||||
The cart grammar and the Zed extension files are 0BSD ( see `LICENSE` ).
|
||||
The PICO-8 Lua grammar is a fork of MIT-licensed `tree-sitter-lua`; the
|
||||
upstream license is preserved at `grammars/pico-8-lua/UPSTREAM-LICENSE.md`
|
||||
and applies to the derived files in that directory.
|
||||
|
||||
Reference in New Issue
Block a user