# Contributing to Nimcheck Pure Nim stdlib, tokenizer-based validation, no external parser dependencies. --- ## Before you start 1. Read [docs/STATUS.md](docs/STATUS.md) — what's done and project scope. 2. Read [README.md](README.md) — architecture and API. 3. Default branch is **`master`**. Open PRs against `master`. 4. CI must pass: `make lint && make test` (see [docs/GITEA.md](docs/GITEA.md)). --- ## Development setup ```bash git clone https://retoor.molodetz.nl/retoor/nimcheck.git cd nimcheck nimble build nimble test ``` Requires **Nim >= 2.0.0**. --- ## Code style - Match existing naming and module layout. - Keep validators simple: tokenize → analyze → report. - **Never throw from validation paths** — catch and return `E9999`. - No new external dependencies without discussion. - Minimal comments; code should be self-explanatory. --- ## Adding a new language ### 1. Types and detection In `src/nimcheck/core/types.nim`: - Add `lfYourlang` to `LanguageFlavor` if missing. - Add string conversion in `$` / `parseFlavor` helpers. In `src/nimcheck/core/detector.nim`: - Add extension mappings to `extensionMap`. - Add shebang entries if applicable. - Add content-scoring keywords (avoid false positives — see entropy/margin rules). ### 2. Tokenizer Create `src/nimcheck/tokenizers/_tokenizer.nim` (or extend `extended_tokenizers.nim` / `cfamily_tokenizers.nim` for CLike langs): ```nim type MyLangTokenizer* = ref object of TokenizerBase method tokenize*(self: MyLangTokenizer) = while self.hasMore(): let posBefore = self.pos # ... lex next token ... self.finishTokenizeStep(posBefore) # REQUIRED — prevents infinite loops self.emitToken(tkEndOfFile, "", self.currentPos()) ``` Rules: - Always call `finishTokenizeStep(posBefore)` at end of each loop iteration. - Use `closeBracket()` for `)`, `]`, `}` — it advances position. - Record errors via `recordError()`, don't raise. ### 3. Validator Create `src/nimcheck/languages/_validator.nim`: ```nim type MyLangValidator* = ref object of ValidatorBase method createTokenizer*(self: MyLangValidator): TokenizerBase = ... method analyzeTokens*(self: MyLangValidator) = procCall ValidatorBase(self).analyzeTokens() # bracket validation # language-specific block/string/structure checks ``` Register in `init()`: ```nim registerValidator("mylang", proc(...) = result = newMyLangValidator(...)) ``` ### 4. Wire up - Import validator in `src/nimcheck.nim` (inside the validator import block). - Add factory case in `src/nimcheck/core/validatorbase.nim` `createValidator()` if needed. ### 5. Tests For each language add: ``` tests/fixtures//valid.* tests/fixtures//invalid.* tests/fixtures//edge_cases.* tests/test_.nim # basic: good code, bad code, files, detectFlavor tests/test__exhaustive.nim ``` Add shared snippets to `tests/test_common.nim` if other tests need them. Import in `tests/test_all.nim`. **Makefile tab pitfall:** In Nim `"""` strings, a line starting with `\t` is often backslash+t (ord 92+116), not TAB (ord 9). Use: ```nim """recipe line""" & "\t" & """command""" ``` ### 6. Documentation Update `README.md` language table and `docs/STATUS.md` if adding a flavor. --- ## Running tests ```bash make test # everything nim c -r tests/test_go.nim # single language make test-nim # via Makefile shortcut nimble test # via nimble task ``` --- ## Pull request checklist - [ ] `make lint` passes - [ ] `make test` passes - [ ] New language has fixtures + basic + exhaustive tests - [ ] `detectFlavor` works for good-code sample in `test_common.nim` - [ ] No debug binaries or `*.log` committed - [ ] README / STATUS updated if behavior or flavors changed --- ## Reporting bugs Include: 1. Nim version (`nim --version`) 2. Input source (minimal repro) 3. Expected vs actual `ValidationResult` 4. Whether it hangs (likely tokenizer loop — check `finishTokenizeStep`) --- ## License By contributing, you agree your changes are licensed under the project MIT license.