← Playground CONTRIBUTING.md
raw

Contributing to NURL

Thanks for considering a contribution. NURL is a young project and the language is still finding its shape, so the bar for "is this worth discussing" is pretty low — open an issue or a PR and we'll figure it out from there.

This document is intentionally short. If something here is unclear or seems wrong, that's a contribution opportunity too.

Ways to help

expected behaviour is plenty. Smaller is better — if you can shrink the repro to the few lines that trigger the issue, that's already most of the fix.

docstrings, and example programs all benefit from extra eyes.

demonstrate idioms, stdlib usage, or interesting algorithms are always welcome.

If you need something useful and general, propose it.

better error messages, missing features from the roadmap — all fair game. Read docs/dev/COMPILER_INTERNALS.md first — it maps the fused walk, the global state, and the safe-change checklist. Open an issue for anything non-trivial so we can align on approach before you write a lot of code.

package management, build integrations — there's plenty of green field.

Asking questions

For real bugs in the compiler, stdlib, or playground, use the GitHub issue tracker.

For open-ended questions, design discussions, "is this a bug or am I holding it wrong", show-and-tell, and anything else community-shaped, the subreddit is a better home: https://www.reddit.com/r/nurllang.

If you're not sure which is right, pick either. We'll redirect if needed and nobody will be grumpy about it.

Filing a good bug report

Issues that include all of the following get resolved much faster:

  1. What you did. A minimal .nu source file (or inline snippet)

that triggers the issue.

  1. What you expected to happen.
  2. What actually happened. Full stderr output if relevant, plus

the produced LLVM IR if you have it (nurlc <file> prints to stdout).

  1. Your environment. OS, clang/LLVM version, and whether you're

running the native compiler, the wasm build, or the playground.

If the issue is in the playground specifically, mention your browser. If it's a regression, "this worked in commit X but breaks at commit Y" is gold.

Suggesting a feature

For language-level changes (new syntax, grammar tweaks, type-system work), open an issue or a subreddit thread first. The grammar is deliberately small and every addition has to earn its keep, so it's worth aligning before anyone writes a parser patch.

For stdlib additions, a short rationale + proposed signatures is enough — we'll iterate on the design in the issue thread.

Pull requests

Split unrelated changes into separate PRs.

consistent conventions; mimic what's nearby rather than introducing new patterns ad-hoc.

`sh ./build.sh cd compiler/tests && ./run_tests.sh ` The bootstrap requires byte-identical LLVM IR on its second pass — if your change is non-deterministic, the build will reject it.

belong in compiler/tests/ as .nu snippets, each with a per-test golden under compiler/tests/outputs/ (record it with compiler/tests/run_tests.sh --update <name>; run a single test with run_tests.sh <name>). Programs that must be rejected go in as diag_*.nu, should_fail_*.nu or borrow_*.nu — and the prefix decides what the golden keeps. diag_* and borrow_* baseline the diagnostic text; should_fail_* records only the single word COMPILE FAIL, so a message behind one can be gutted without a golden moving. If the wording is the point — and for an error message it usually is — use diag_*.

thin overview that links to topic docs under docs/; update the relevant one (docs/spec.md, docs/LIMITATIONS.md, docs/NETWORKING.md, …) or the EBNF grammar in spec/, and add a CHANGELOG.md entry, as appropriate. The code in the docs is compiled: every nurl ` block must build (or be rejected with the diagnostic it is annotated with), and in docs/ every fence names its language. Run ./tools/check_doc_examples.sh — the annotations are in docs/TOOLING.md.

for small fixes; multi-line bodies are welcome for larger changes that warrant context.

There's no formal review SLA — we'll try to look at PRs reasonably promptly, but be patient if it takes a few days.

Development setup

The full bootstrap chain:

git clone https://github.com/nurl-lang/nurl
cd nurl

# Bootstrap the self-hosted compiler (committed snapshot IR → NURL → NURL
# fixed point). Compiles the C runtime for you.
./build.sh

Requirements: clang/LLVM 15+. Nothing else — the bootstrap snapshot is committed LLVM IR (compiler/nurlc_lastgood.ll) that clang links directly into a working boot compiler. Windows users have build.bat; macOS builds with Homebrew LLVM and coreutils (brew install llvm coreutils). The Apple Silicon host path runs in CI; Intel host builds remain unverified. The POSIX corpus requires timeout or gtimeout so compiler hangs are bounded. See docs/PLATFORMS.md for the full matrix.

Work beyond the compiler proper — the unikernel (QEMU, GRUB, xorriso), the cross-compiled targets (zig), the Cloudflare Workers (Node, pnpm, wrangler), the benchmark comparisons (Rust) — needs more than clang. If you would rather not install all of that on your machine, there is a prepared container:

./containers/dev/run.sh          # shell with the whole toolchain

See containers/dev/README.md; VS Code and Codespaces users can "Reopen in Container" instead.

Compile and run a single program:

./nurl.sh examples/fizzbuzz.nu

The browser playground lives under nurlapi/; see its README.md (and docs/PLAYGROUND.md) for the container/dev setup.

Style conventions

Run nurlfmt --write before pushing. It applies the canonical .nu format (indentation, spacing, import grouping) and is idempotent and IR-preserving; CI hard-gates on nurlfmt --check. ./build.sh also activates the pre-commit hook (.githooks/pre-commit, via core.hooksPath), which keeps commits canonical for you: a fully-staged .nu file is nurlfmt --write'n and re-staged automatically, while a partially-staged file that isn't canonical blocks the commit (run nurlfmt --write and re-stage it — the hook won't rewrite it, to avoid staging your unstaged edits). The hook skips cleanly if build/nurlfmt isn't built yet, excludes bench/, and can be bypassed once with git commit --no-verify. See docs/FORMAT.md.

A few conventions the formatter does not enforce, so apply them by hand:

for types, SCREAMING_CASE for top-level constants.

obviously does; document why when the answer isn't immediately obvious from context.

Licensing

NURL is dual-licensed under MIT or Apache-2.0, at your option. By submitting a contribution you agree that your work may be distributed under both licenses.

There's no CLA. Contributions are made under the project's existing licensing; please don't include code you don't have the right to contribute.

Code of conduct

Be decent to each other. Disagree about the work, not the person. Help newcomers. Assume good faith.

If something specific goes wrong and isn't handled well in public, contact a project maintainer privately and we'll sort it out.

Thanks

Every issue, PR, comment, example, and question helps shape what NURL becomes. We appreciate you taking the time.