Update
This commit is contained in:
parent
3dacbb3ab0
commit
608974021d
22
.gitignore
vendored
22
.gitignore
vendored
@ -4,7 +4,7 @@ tests/test_json_exhaustive
|
||||
tests/test_toml_exhaustive
|
||||
tests/test_yaml_exhaustive
|
||||
*.bak*
|
||||
# ── Nim ──────────────────────────────────────────────────────────────────
|
||||
# -- Nim ------------------------------------------------------------------
|
||||
nimcache/
|
||||
nimblecache/
|
||||
*.exe
|
||||
@ -15,10 +15,10 @@ nimblecache/
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# ── Binaries (compiled output) ───────────────────────────────────────────
|
||||
# -- Binaries (compiled output) -------------------------------------------
|
||||
bin/
|
||||
|
||||
# ── Test compiled binaries (source is .nim, binaries have no extension) ──
|
||||
# -- Test compiled binaries (source is .nim, binaries have no extension) --
|
||||
tests/test_all
|
||||
tests/test_bash
|
||||
tests/test_nim
|
||||
@ -35,16 +35,16 @@ tests/test_mixed
|
||||
tests/test_config
|
||||
tests/test_fuzz
|
||||
|
||||
# ── Logs ─────────────────────────────────────────────────────────────────
|
||||
# -- Logs -----------------------------------------------------------------
|
||||
*.log
|
||||
|
||||
# ── Python cache ─────────────────────────────────────────────────────────
|
||||
# -- Python cache ---------------------------------------------------------
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
|
||||
# ── OS files ─────────────────────────────────────────────────────────────
|
||||
# -- OS files -------------------------------------------------------------
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
@ -54,7 +54,7 @@ ehthumbs.db
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# ── Editor files ─────────────────────────────────────────────────────────
|
||||
# -- Editor files ---------------------------------------------------------
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
@ -65,23 +65,23 @@ desktop.ini
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# ── Environment / secrets ────────────────────────────────────────────────
|
||||
# -- Environment / secrets ------------------------------------------------
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
*.env
|
||||
!.env.example
|
||||
|
||||
# ── Temporary / backup files ─────────────────────────────────────────────
|
||||
# -- Temporary / backup files ---------------------------------------------
|
||||
/tmp/
|
||||
*.tmp
|
||||
*.temp
|
||||
*.pid
|
||||
|
||||
# ── dpc agent logs ───────────────────────────────────────────────────────
|
||||
# -- dpc agent logs -------------------------------------------------------
|
||||
dpc.log
|
||||
|
||||
# ── Coverage reports ─────────────────────────────────────────────────────
|
||||
# -- Coverage reports -----------------------------------------------------
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
|
||||
46
README.md
46
README.md
@ -88,8 +88,8 @@ Each validator:
|
||||
|
||||
1. Creates its tokenizer and runs `tokenize()`
|
||||
2. Collects tokenizer errors
|
||||
3. Runs `analyzeTokens()` — language-specific structural analysis
|
||||
4. Runs `buildModuleInfo()` — populates `ModuleInfo` with imports, functions, classes
|
||||
3. Runs `analyzeTokens()` - language-specific structural analysis
|
||||
4. Runs `buildModuleInfo()` - populates `ModuleInfo` with imports, functions, classes
|
||||
5. Computes validity, warning/info counts, duration
|
||||
|
||||
Validators register themselves via a factory pattern at import time. The registry maps flavor strings (e.g., `"python"`, `"bash"`) to constructors.
|
||||
@ -674,7 +674,7 @@ Every validation function follows the same contract:
|
||||
| `sourcePath: string = ""` (optional) | `ValidationResult.durationMs: float` |
|
||||
|
||||
**Guarantees**:
|
||||
- Never throws — all exceptions are caught and returned as `E9999` errors
|
||||
- Never throws - all exceptions are caught and returned as `E9999` errors
|
||||
- Empty source returns `E0009` (or `valid: true` for YAML/TOML where empty is valid)
|
||||
- File not found returns `E0011`
|
||||
- Unknown flavor with no detection possible returns an `E0007` error
|
||||
@ -686,8 +686,8 @@ Every validation function follows the same contract:
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `nim_tokenizer.nim` — `NimTokenizer` |
|
||||
| Validator | `nim_validator.nim` — `NimValidator` |
|
||||
| Tokenizer | `nim_tokenizer.nim` - `NimTokenizer` |
|
||||
| Validator | `nim_validator.nim` - `NimValidator` |
|
||||
| Keywords | 80+ (addr, and, as, block, break, case, const, converter, defer, discard, elif, else, enum, except, export, finally, for, from, func, if, import, in, include, iterator, let, macro, method, mixin, mod, nil, not, object, of, or, proc, ptr, raise, ref, return, template, try, tuple, type, using, var, when, while, with, yield, etc.) |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -707,8 +707,8 @@ Structural extraction: imports (`from X import Y`), functions (`proc`, `func`, `
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `bash_tokenizer.nim` — `BashTokenizer` |
|
||||
| Validator | `bash_validator.nim` — `BashValidator` |
|
||||
| Tokenizer | `bash_tokenizer.nim` - `BashTokenizer` |
|
||||
| Validator | `bash_validator.nim` - `BashValidator` |
|
||||
| Keywords | 38+ (if, then, else, elif, fi, case, esac, for, while, until, do, done, in, function, select, time, return, break, continue, exit, declare, local, export, readonly, unset, eval, exec, let, source, shift, trap, type, typeset, ulimit, umask, alias, read, echo) |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -728,8 +728,8 @@ Special features: shebang detection (`#!`), heredoc detection (`<<EOF`), variabl
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `python_tokenizer.nim` — `PythonTokenizer` |
|
||||
| Validator | `python_validator.nim` — `PythonValidator` |
|
||||
| Tokenizer | `python_tokenizer.nim` - `PythonTokenizer` |
|
||||
| Validator | `python_validator.nim` - `PythonValidator` |
|
||||
| Keywords | 33 (False, None, True, and, as, assert, async, await, break, class, continue, def, del, elif, else, except, finally, for, from, global, if, import, in, is, lambda, nonlocal, not, or, pass, raise, return, try, while, with, yield) |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -749,8 +749,8 @@ Structural extraction: imports (`import X`, `from X import Y`), functions (`def`
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `javascript_tokenizer.nim` — `JavaScriptTokenizer` |
|
||||
| Validator | `javascript_validator.nim` — `JavaScriptValidator` |
|
||||
| Tokenizer | `javascript_tokenizer.nim` - `JavaScriptTokenizer` |
|
||||
| Validator | `javascript_validator.nim` - `JavaScriptValidator` |
|
||||
| Keywords | 60+ (async, await, break, case, catch, class, const, continue, debugger, default, delete, do, else, export, extends, false, finally, for, function, if, import, in, instanceof, let, new, null, of, return, super, switch, static, this, throw, true, try, typeof, var, void, while, with, yield + TypeScript: interface, type, declare, module, namespace, abstract, readonly, as, any, boolean, number, string, never, keyof, infer, unknown) |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -768,9 +768,9 @@ Structural extraction: imports/exports, functions (`function`, `async`), classes
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `php_tokenizer.nim` — `PHPTokenizer` |
|
||||
| Validator | `php_validator.nim` — `PHPValidator` |
|
||||
| Mode switching | Tracks `inPhpMode` flag — toggles on `<?php`/`<?` and `?>` |
|
||||
| Tokenizer | `php_tokenizer.nim` - `PHPTokenizer` |
|
||||
| Validator | `php_validator.nim` - `PHPValidator` |
|
||||
| Mode switching | Tracks `inPhpMode` flag - toggles on `<?php`/`<?` and `?>` |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
|------------|-----------|----------|------|
|
||||
@ -785,8 +785,8 @@ Structural extraction: functions (`function`, `fn`), classes (`class`), imports/
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `html_tokenizer.nim` — `HTMLTokenizer` |
|
||||
| Validator | `html_validator.nim` — `HTMLValidator` |
|
||||
| Tokenizer | `html_tokenizer.nim` - `HTMLTokenizer` |
|
||||
| Validator | `html_validator.nim` - `HTMLValidator` |
|
||||
| Void elements | 14: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -801,8 +801,8 @@ Special features: tag stack balancing, void element awareness (no closing tag ne
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Tokenizer | `jinja_tokenizer.nim` — `JinjaTokenizer` |
|
||||
| Validator | `jinja_validator.nim` — `JinjaValidator` |
|
||||
| Tokenizer | `jinja_tokenizer.nim` - `JinjaTokenizer` |
|
||||
| Validator | `jinja_validator.nim` - `JinjaValidator` |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
|------------|-----------|----------|------|
|
||||
@ -820,7 +820,7 @@ Special features: tracks 12 block tag types (`for`, `if`, `block`, `macro`, `fil
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Validator | `config_validators.nim` — `JSONValidator` |
|
||||
| Validator | `config_validators.nim` - `JSONValidator` |
|
||||
| Method | Uses Nim's built-in `parseJson()` for full validation |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -833,7 +833,7 @@ Special features: tracks 12 block tag types (`for`, `if`, `block`, `macro`, `fil
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Validator | `config_validators.nim` — `YAMLValidator` |
|
||||
| Validator | `config_validators.nim` - `YAMLValidator` |
|
||||
| Method | Line-by-line structural analysis |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -849,7 +849,7 @@ Note: Empty YAML (only whitespace/comments) is `valid: true`.
|
||||
|
||||
| Aspect | Details |
|
||||
|--------|---------|
|
||||
| Validator | `config_validators.nim` — `TOMLValidator` |
|
||||
| Validator | `config_validators.nim` - `TOMLValidator` |
|
||||
| Method | Line-by-line structural analysis |
|
||||
|
||||
| Error Code | Condition | Severity | Hint |
|
||||
@ -912,7 +912,7 @@ Supported flavors:
|
||||
| 0 | Validation successful (or help/version requested) |
|
||||
| Non-zero (Nim default) | Runtime error |
|
||||
|
||||
Note: The `validate` command always exits 0 on the Nim level; the `valid` field in the output indicates pass/fail. This is by design — Nimcheck never crashes.
|
||||
Note: The `validate` command always exits 0 on the Nim level; the `valid` field in the output indicates pass/fail. This is by design - Nimcheck never crashes.
|
||||
|
||||
---
|
||||
|
||||
@ -1084,7 +1084,7 @@ See `LESSONS_LEARNED.md` for the complete checklist (Section 14). The minimum st
|
||||
|
||||
### Design Principles
|
||||
|
||||
- **KISS**: Keep validators simple — tokenize, analyze brackets, report
|
||||
- **KISS**: Keep validators simple - tokenize, analyze brackets, report
|
||||
- **Never crash**: Every validation path in a try/except
|
||||
- **No external deps**: Pure Nim standard library
|
||||
- **DRY**: Common logic in `tokenizerbase.nim` and `validatorbase.nim`
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
import std/[json, strutils, os, strformat, sequtils, algorithm]
|
||||
|
||||
# Core types and base classes — imported and re-exported for public API consumers
|
||||
# Core types and base classes - imported and re-exported for public API consumers
|
||||
import nimcheck/core/types
|
||||
import nimcheck/core/tokenizerbase
|
||||
import nimcheck/core/validatorbase
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
## Validator registration — called at module load time.
|
||||
## Validator registration - called at module load time.
|
||||
##
|
||||
## Each language validator registers itself here.
|
||||
## This file is imported by the main nimcheck.nim module.
|
||||
|
||||
@ -202,7 +202,7 @@ proc validateBrackets*(self: TokenizerBase): seq[ValidationError] =
|
||||
let hintStr = &"Add a closing '{pairStr}' before this point"
|
||||
let err = newValidationError(
|
||||
esError,
|
||||
&"Unclosed '{ch}' — expected matching '{pairStr}'",
|
||||
&"Unclosed '{ch}' - expected matching '{pairStr}'",
|
||||
ErrUnclosedBracket,
|
||||
pos,
|
||||
SourceRange(startPos: pos, endPos: pos),
|
||||
@ -215,12 +215,12 @@ proc validateBrackets*(self: TokenizerBase): seq[ValidationError] =
|
||||
self.errors.add(result)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Abstract interface — subclasses must implement
|
||||
# Abstract interface - subclasses must implement
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
method tokenize*(self: TokenizerBase) {.base, gcsafe.} =
|
||||
## Tokenize the entire source. Must be overridden.
|
||||
raise newException(ValueError, "tokenize() not implemented — subclass must override")
|
||||
raise newException(ValueError, "tokenize() not implemented - subclass must override")
|
||||
|
||||
proc getAllTokens*(self: TokenizerBase): seq[Token] =
|
||||
## Get all tokens after tokenization.
|
||||
|
||||
@ -36,7 +36,7 @@ proc newValidatorBase*(
|
||||
result.result.flavor = options.flavor
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Template methods — subclasses override
|
||||
# Template methods - subclasses override
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
method createTokenizer*(self: ValidatorBase): TokenizerBase {.base, gcsafe.} =
|
||||
@ -121,7 +121,7 @@ method validate*(self: ValidatorBase): ValidationResult {.base, gcsafe.} =
|
||||
self.result.moduleInfo.linesOfCode = self.source.count('\n') + 1
|
||||
|
||||
except Exception as e:
|
||||
# Never crash — always report useful error
|
||||
# Never crash - always report useful error
|
||||
self.result.valid = false
|
||||
self.result.errors.add(newValidationError(
|
||||
esCritical,
|
||||
@ -129,7 +129,7 @@ method validate*(self: ValidatorBase): ValidationResult {.base, gcsafe.} =
|
||||
"E9999",
|
||||
newSourcePosition(0, 0, 0),
|
||||
newSourceRange(newSourcePosition(0, 0, 0), newSourcePosition(0, 0, 0)),
|
||||
hint = "This is a bug in the validator — please report it"
|
||||
hint = "This is a bug in the validator - please report it"
|
||||
))
|
||||
when defined(nimcheckDebug):
|
||||
if self.options.debugMode:
|
||||
@ -162,7 +162,7 @@ proc isValid*(self: ValidatorBase): bool =
|
||||
self.result.valid
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Factory — create correct validator for a flavor
|
||||
# Factory - create correct validator for a flavor
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
type
|
||||
|
||||
@ -77,7 +77,7 @@ method tokenize*(self: PHPTokenizer) =
|
||||
self.emitToken(tkNewline, "\n", startPos)
|
||||
continue
|
||||
|
||||
# Inside PHP mode — check for exit
|
||||
# Inside PHP mode - check for exit
|
||||
if self.peekString(2) == "?>" and self.inPhpMode:
|
||||
for _ in 1..2: discard self.advance()
|
||||
self.emitToken(tkDirective, "?>", startPos)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user