Cart grammar: tolerate leading blank lines before the magic header
`extras: $ => []` in the cart grammar made the parser fail at byte 0 on any whitespace-only or empty line before `pico-8 cartridge //...`. Real PICO-8 carts always start with the header at byte 0 so this rarely surfaced in production, but it ( a ) broke the `tree-sitter test` corpus harness, which prepends a newline to each fixture, and ( b ) would mis-flag a hand-edited cart that gained an accidental blank line up top. Fix: prefix the `cartridge` rule with `repeat($._blank_line)` and add a hidden `_blank_line` token matching `[ \t]*\n`. Junk content before the header ( a non-blank, non-magic line ) still produces an ERROR. Restores the test corpus that was dropped in v0.1 ( previously failing on this same edge case ) and adds a fixture for the unknown_section fallback while the corpus is being rebuilt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -149,10 +149,15 @@ dev extension to pick up changes.
|
|||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
|
|
||||||
Sample carts live under `examples/`. Use `tree-sitter parse` directly for
|
Sample carts live under `examples/`; parse them directly with
|
||||||
verification — corpus-style `tree-sitter test` is not currently set up
|
`tree-sitter parse <file>` for ad-hoc checks.
|
||||||
because the cart grammar's strict `extras: []` doesn't tolerate the
|
|
||||||
leading newline that the test harness prepends to each fixture.
|
The cart grammar has a corpus under `grammars/p8-cart/test/corpus/` —
|
||||||
|
run `( cd grammars/p8-cart && npx tree-sitter test )`. The corpus
|
||||||
|
covers the empty-section skeleton, normal Lua content, the case where
|
||||||
|
a Lua identifier resembles a section marker ( e.g. `local __foo__ = 1`
|
||||||
|
must remain a `line`, not be re-tokenized as a marker ), and the
|
||||||
|
fallback `unknown_section` rule.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
|
|||||||
@@ -18,16 +18,31 @@
|
|||||||
module.exports = grammar({
|
module.exports = grammar({
|
||||||
name: 'p8_cart',
|
name: 'p8_cart',
|
||||||
|
|
||||||
// Whitespace is significant inside hex sections, so we don't skip it.
|
// Whitespace is significant inside hex sections, so we don't skip it
|
||||||
|
// globally. Tolerance for stray leading blanks before the magic header
|
||||||
|
// is added explicitly via the `repeat($._blank_line)` at the top of
|
||||||
|
// `cartridge` ( see below ).
|
||||||
extras: $ => [],
|
extras: $ => [],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
cartridge: $ => seq(
|
cartridge: $ => seq(
|
||||||
|
// Tolerate stray whitespace / blank lines before the magic header.
|
||||||
|
// Real PICO-8 carts begin with the header on byte 0, but allowing
|
||||||
|
// a leading run of blanks ( a ) lets the `tree-sitter test` corpus
|
||||||
|
// framework, which prepends a newline to each fixture, run cleanly
|
||||||
|
// and ( b ) keeps the parser robust against a hand-edited cart that
|
||||||
|
// gained an accidental blank line up top.
|
||||||
|
repeat($._blank_line),
|
||||||
optional($.header),
|
optional($.header),
|
||||||
optional($.version),
|
optional($.version),
|
||||||
repeat($.section),
|
repeat($.section),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// A line that has no content other than horizontal whitespace and a
|
||||||
|
// newline. Hidden ( underscore prefix ) so it does not appear in the
|
||||||
|
// syntax tree.
|
||||||
|
_blank_line: $ => token(/[ \t]*\n/),
|
||||||
|
|
||||||
header: $ => /pico-8 cartridge \/\/[^\n]*\n/,
|
header: $ => /pico-8 cartridge \/\/[^\n]*\n/,
|
||||||
version: $ => /version[ \t]+\d+\n/,
|
version: $ => /version[ \t]+\d+\n/,
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,13 @@
|
|||||||
"cartridge": {
|
"cartridge": {
|
||||||
"type": "SEQ",
|
"type": "SEQ",
|
||||||
"members": [
|
"members": [
|
||||||
|
{
|
||||||
|
"type": "REPEAT",
|
||||||
|
"content": {
|
||||||
|
"type": "SYMBOL",
|
||||||
|
"name": "_blank_line"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "CHOICE",
|
"type": "CHOICE",
|
||||||
"members": [
|
"members": [
|
||||||
@@ -38,6 +45,13 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"_blank_line": {
|
||||||
|
"type": "TOKEN",
|
||||||
|
"content": {
|
||||||
|
"type": "PATTERN",
|
||||||
|
"value": "[ \\t]*\\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"type": "PATTERN",
|
"type": "PATTERN",
|
||||||
"value": "pico-8 cartridge \\/\\/[^\\n]*\\n"
|
"value": "pico-8 cartridge \\/\\/[^\\n]*\\n"
|
||||||
|
|||||||
+1042
-889
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
|||||||
|
==================
|
||||||
|
empty cart skeleton
|
||||||
|
==================
|
||||||
|
|
||||||
|
pico-8 cartridge // http://www.pico-8.com
|
||||||
|
version 42
|
||||||
|
__lua__
|
||||||
|
__gfx__
|
||||||
|
__map__
|
||||||
|
__sfx__
|
||||||
|
__music__
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(cartridge
|
||||||
|
(header)
|
||||||
|
(version)
|
||||||
|
(section (lua_section (lua_marker)))
|
||||||
|
(section (gfx_section (gfx_marker)))
|
||||||
|
(section (map_section (map_marker)))
|
||||||
|
(section (sfx_section (sfx_marker)))
|
||||||
|
(section (music_section (music_marker))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
cart with lua content
|
||||||
|
==================
|
||||||
|
|
||||||
|
pico-8 cartridge // http://www.pico-8.com
|
||||||
|
version 42
|
||||||
|
__lua__
|
||||||
|
function _draw()
|
||||||
|
cls()
|
||||||
|
end
|
||||||
|
__gfx__
|
||||||
|
00000000
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(cartridge
|
||||||
|
(header)
|
||||||
|
(version)
|
||||||
|
(section
|
||||||
|
(lua_section
|
||||||
|
(lua_marker)
|
||||||
|
(lua_content
|
||||||
|
(line)
|
||||||
|
(line)
|
||||||
|
(line))))
|
||||||
|
(section
|
||||||
|
(gfx_section
|
||||||
|
(gfx_marker)
|
||||||
|
(body
|
||||||
|
(line)))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
lua identifier resembling section marker
|
||||||
|
==================
|
||||||
|
|
||||||
|
pico-8 cartridge // http://www.pico-8.com
|
||||||
|
version 42
|
||||||
|
__lua__
|
||||||
|
local __foo__ = 1
|
||||||
|
local s = "__lua__"
|
||||||
|
__gfx__
|
||||||
|
00
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(cartridge
|
||||||
|
(header)
|
||||||
|
(version)
|
||||||
|
(section
|
||||||
|
(lua_section
|
||||||
|
(lua_marker)
|
||||||
|
(lua_content
|
||||||
|
(line)
|
||||||
|
(line))))
|
||||||
|
(section
|
||||||
|
(gfx_section
|
||||||
|
(gfx_marker)
|
||||||
|
(body
|
||||||
|
(line)))))
|
||||||
|
|
||||||
|
==================
|
||||||
|
unknown section name
|
||||||
|
==================
|
||||||
|
|
||||||
|
pico-8 cartridge // http://www.pico-8.com
|
||||||
|
version 42
|
||||||
|
__lua__
|
||||||
|
__future_section__
|
||||||
|
opaque body
|
||||||
|
__gfx__
|
||||||
|
00
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
(cartridge
|
||||||
|
(header)
|
||||||
|
(version)
|
||||||
|
(section (lua_section (lua_marker)))
|
||||||
|
(section
|
||||||
|
(unknown_section
|
||||||
|
(section_marker)
|
||||||
|
(body (line))))
|
||||||
|
(section
|
||||||
|
(gfx_section
|
||||||
|
(gfx_marker)
|
||||||
|
(body (line)))))
|
||||||
Reference in New Issue
Block a user