1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 00:45:38 +00:00
nescript/Cargo.toml

61 lines
1.4 KiB
TOML
Raw Normal View History

Implement NEScript compiler Milestone 1 ("Hello Sprite") Complete implementation of the NEScript compiler pipeline for M1: - Lexer: full tokenization with hex/binary/decimal literals, all keywords, operators - Parser: recursive descent with Pratt expression parsing (M1 subset) - Analyzer: symbol resolution, type checking, memory allocation - 6502 Assembler: full opcode encoding table (~150 valid combinations) - Code Generator: AST → 6502 instructions (direct, no IR for M1) - Runtime: NES hardware init, NMI handler, controller read, OAM DMA - Linker: NROM layout, vector table, palette loading, CHR data - ROM Builder: iNES header generation, PRG/CHR padding - CLI: `build` and `check` subcommands via clap 143 tests across all modules: - 22 lexer tests (literals, keywords, operators, error recovery) - 18 parser tests (expressions, statements, game structure, errors) - 7 analyzer tests (symbol resolution, memory allocation, transitions) - 30 assembler tests (every addressing mode, label resolution) - 7 codegen tests (var init, arithmetic, buttons, draw, comparisons) - 11 runtime tests (init sequence, NMI handler, controller read) - 10 ROM builder tests (iNES format, mirroring, banking, validation) - 5 linker tests (vector table, CHR data, palette loading) - 7 integration tests (end-to-end compilation, error detection) CI: GitHub Actions for check, fmt, clippy, test Pre-commit: script for local fmt + clippy + test validation https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
2026-04-11 22:07:56 +00:00
[package]
name = "nescript"
version = "0.1.0"
edition = "2021"
description = "A statically-typed compiler for NES game development"
license = "MIT"
platformer: end-to-end side-scroller demo + three runtime bug fixes Adds `examples/platformer.ne`, a full side-scrolling game that exercises nearly every subsystem of the compiler in one program: custom CHR tileset, 32×30 background nametable with per-region attribute palettes, 2×2 metasprite hero with gravity/jump physics, wrap-around horizontal scrolling, moving enemies, coin pickups, user-declared SFX + music, and a Title → Playing state machine with autopilot so the headless jsnes harness captures real gameplay at frame 180. Tile art + nametable are generated by `scripts/gen_platformer_tiles.rs` (`cargo run --bin gen_platformer_tiles`). Building this out uncovered three independent runtime bugs that together made the example render as black-on-black smileys. All three are fixed in this commit: 1. **`gen_init` enabled sprite rendering before the linker's initial palette/background load runs.** The PPU's v-register auto-increments on every `$2007` write *during active rendering*, so the palette load (32 B) and nametable load (1024 B) were scrambled past the first ~72 bytes — every existing program with a `background Level { ... }` block was silently rendering zero-filled VRAM. Fix: leave `PPU_MASK = 0` at the end of `gen_init` and emit a new `gen_enable_rendering` call *after* all initial VRAM writes complete. 2. **Audio tick corrupted `ZP_CURRENT_STATE`.** The audio driver's period-table lookup reused `$02/$03` as a temporary indirect pointer with a comment claiming the slots were free because the tick doesn't call mul/div. But `$03` is also `ZP_CURRENT_STATE` used by the state dispatch loop, so every music note silently overwrote the state index with the high byte of `__period_table` (`0xC5` in the platformer ROM), wedging the state machine forever. Fix: `gen_nmi` now PHAs `$02/$03` on entry and PLA-restores them on exit, and the audio tick JSR moves inside that save/restore window (it used to be spliced by the linker *before* the register saves, so even A/X/Y were technically being trashed pre-save). Only `audio_demo`'s audio hash shifts (its note timings move a few cycles); every other golden is unchanged. 3. **Sub-palette mirroring footgun.** Writing a 32-byte palette blob sequentially causes the sprite sub-palettes' "index 0" slots at `$3F10/$3F14/$3F18/$3F1C` to clobber the background universal colour at `$3F00/$3F04/$3F08/$3F0C` via NES hardware mirroring. The example's palette sets all eight first bytes to `$22` (sky blue) for this reason; `docs/future-work.md` picks up a TODO to warn on inconsistent first-byte values in the analyzer. Also: - `docs/platformer.gif` — 6-second recording of the example running in jsnes, generated by the new `tests/emulator/record_gif.mjs` puppeteer helper (encodes via `gifenc`, committed as a dev-dependency under `tests/emulator/package.json`). - README / examples/README tables and the 497-test count are updated to cover the new example. https://claude.ai/code/session_01BcCcHi6FUmTh8jC7UgkA3A
2026-04-13 13:04:26 +00:00
default-run = "nescript"
[[bin]]
name = "nescript"
path = "src/main.rs"
# One-shot tile / nametable generator for examples/platformer.ne. Kept in-tree
# (rather than as a Python script) so the repo stays single-language.
[[bin]]
name = "gen_platformer_tiles"
path = "scripts/gen_platformer_tiles.rs"
Implement NEScript compiler Milestone 1 ("Hello Sprite") Complete implementation of the NEScript compiler pipeline for M1: - Lexer: full tokenization with hex/binary/decimal literals, all keywords, operators - Parser: recursive descent with Pratt expression parsing (M1 subset) - Analyzer: symbol resolution, type checking, memory allocation - 6502 Assembler: full opcode encoding table (~150 valid combinations) - Code Generator: AST → 6502 instructions (direct, no IR for M1) - Runtime: NES hardware init, NMI handler, controller read, OAM DMA - Linker: NROM layout, vector table, palette loading, CHR data - ROM Builder: iNES header generation, PRG/CHR padding - CLI: `build` and `check` subcommands via clap 143 tests across all modules: - 22 lexer tests (literals, keywords, operators, error recovery) - 18 parser tests (expressions, statements, game structure, errors) - 7 analyzer tests (symbol resolution, memory allocation, transitions) - 30 assembler tests (every addressing mode, label resolution) - 7 codegen tests (var init, arithmetic, buttons, draw, comparisons) - 11 runtime tests (init sequence, NMI handler, controller read) - 10 ROM builder tests (iNES format, mirroring, banking, validation) - 5 linker tests (vector table, CHR data, palette loading) - 7 integration tests (end-to-end compilation, error detection) CI: GitHub Actions for check, fmt, clippy, test Pre-commit: script for local fmt + clippy + test validation https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
2026-04-11 22:07:56 +00:00
[dependencies]
clap = { version = "4", features = ["derive"] }
ariadne = "0.4"
image = { version = "0.25", default-features = false, features = ["png"] }
Implement NEScript compiler Milestone 1 ("Hello Sprite") Complete implementation of the NEScript compiler pipeline for M1: - Lexer: full tokenization with hex/binary/decimal literals, all keywords, operators - Parser: recursive descent with Pratt expression parsing (M1 subset) - Analyzer: symbol resolution, type checking, memory allocation - 6502 Assembler: full opcode encoding table (~150 valid combinations) - Code Generator: AST → 6502 instructions (direct, no IR for M1) - Runtime: NES hardware init, NMI handler, controller read, OAM DMA - Linker: NROM layout, vector table, palette loading, CHR data - ROM Builder: iNES header generation, PRG/CHR padding - CLI: `build` and `check` subcommands via clap 143 tests across all modules: - 22 lexer tests (literals, keywords, operators, error recovery) - 18 parser tests (expressions, statements, game structure, errors) - 7 analyzer tests (symbol resolution, memory allocation, transitions) - 30 assembler tests (every addressing mode, label resolution) - 7 codegen tests (var init, arithmetic, buttons, draw, comparisons) - 11 runtime tests (init sequence, NMI handler, controller read) - 10 ROM builder tests (iNES format, mirroring, banking, validation) - 5 linker tests (vector table, CHR data, palette loading) - 7 integration tests (end-to-end compilation, error detection) CI: GitHub Actions for check, fmt, clippy, test Pre-commit: script for local fmt + clippy + test validation https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
2026-04-11 22:07:56 +00:00
[dev-dependencies]
pretty_assertions = "1"
criterion = "0.5"
[[bench]]
name = "compile"
harness = false
Implement NEScript compiler Milestone 1 ("Hello Sprite") Complete implementation of the NEScript compiler pipeline for M1: - Lexer: full tokenization with hex/binary/decimal literals, all keywords, operators - Parser: recursive descent with Pratt expression parsing (M1 subset) - Analyzer: symbol resolution, type checking, memory allocation - 6502 Assembler: full opcode encoding table (~150 valid combinations) - Code Generator: AST → 6502 instructions (direct, no IR for M1) - Runtime: NES hardware init, NMI handler, controller read, OAM DMA - Linker: NROM layout, vector table, palette loading, CHR data - ROM Builder: iNES header generation, PRG/CHR padding - CLI: `build` and `check` subcommands via clap 143 tests across all modules: - 22 lexer tests (literals, keywords, operators, error recovery) - 18 parser tests (expressions, statements, game structure, errors) - 7 analyzer tests (symbol resolution, memory allocation, transitions) - 30 assembler tests (every addressing mode, label resolution) - 7 codegen tests (var init, arithmetic, buttons, draw, comparisons) - 11 runtime tests (init sequence, NMI handler, controller read) - 10 ROM builder tests (iNES format, mirroring, banking, validation) - 5 linker tests (vector table, CHR data, palette loading) - 7 integration tests (end-to-end compilation, error detection) CI: GitHub Actions for check, fmt, clippy, test Pre-commit: script for local fmt + clippy + test validation https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
2026-04-11 22:07:56 +00:00
[profile.release]
lto = true
strip = true
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
all = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
# Allow patterns common in compiler code
module_name_repetitions = "allow"
must_use_candidate = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
cast_possible_truncation = "allow"
wildcard_imports = "allow"
option_if_let_else = "allow"
match_same_arms = "allow"
unnested_or_patterns = "allow"
missing_const_for_fn = "allow"
too_many_lines = "allow"
single_match_else = "allow"
unnecessary_wraps = "allow"
unused_self = "allow"
enum_glob_use = "allow"
manual_assert = "allow"
vec_init_then_push = "allow"
struct_field_names = "allow"