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_toml_exhaustive
|
||||||
tests/test_yaml_exhaustive
|
tests/test_yaml_exhaustive
|
||||||
*.bak*
|
*.bak*
|
||||||
# ── Nim ──────────────────────────────────────────────────────────────────
|
# -- Nim ------------------------------------------------------------------
|
||||||
nimcache/
|
nimcache/
|
||||||
nimblecache/
|
nimblecache/
|
||||||
*.exe
|
*.exe
|
||||||
@ -15,10 +15,10 @@ nimblecache/
|
|||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|
||||||
# ── Binaries (compiled output) ───────────────────────────────────────────
|
# -- Binaries (compiled output) -------------------------------------------
|
||||||
bin/
|
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_all
|
||||||
tests/test_bash
|
tests/test_bash
|
||||||
tests/test_nim
|
tests/test_nim
|
||||||
@ -35,16 +35,16 @@ tests/test_mixed
|
|||||||
tests/test_config
|
tests/test_config
|
||||||
tests/test_fuzz
|
tests/test_fuzz
|
||||||
|
|
||||||
# ── Logs ─────────────────────────────────────────────────────────────────
|
# -- Logs -----------------------------------------------------------------
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# ── Python cache ─────────────────────────────────────────────────────────
|
# -- Python cache ---------------------------------------------------------
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
*.pyd
|
*.pyd
|
||||||
|
|
||||||
# ── OS files ─────────────────────────────────────────────────────────────
|
# -- OS files -------------------------------------------------------------
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store?
|
.DS_Store?
|
||||||
._*
|
._*
|
||||||
@ -54,7 +54,7 @@ ehthumbs.db
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
|
||||||
# ── Editor files ─────────────────────────────────────────────────────────
|
# -- Editor files ---------------------------------------------------------
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
@ -65,23 +65,23 @@ desktop.ini
|
|||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
# ── Environment / secrets ────────────────────────────────────────────────
|
# -- Environment / secrets ------------------------------------------------
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
*.env
|
*.env
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
# ── Temporary / backup files ─────────────────────────────────────────────
|
# -- Temporary / backup files ---------------------------------------------
|
||||||
/tmp/
|
/tmp/
|
||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
*.pid
|
*.pid
|
||||||
|
|
||||||
# ── dpc agent logs ───────────────────────────────────────────────────────
|
# -- dpc agent logs -------------------------------------------------------
|
||||||
dpc.log
|
dpc.log
|
||||||
|
|
||||||
# ── Coverage reports ─────────────────────────────────────────────────────
|
# -- Coverage reports -----------------------------------------------------
|
||||||
*.gcda
|
*.gcda
|
||||||
*.gcno
|
*.gcno
|
||||||
*.gcov
|
*.gcov
|
||||||
|
|||||||
46
README.md
46
README.md
@ -88,8 +88,8 @@ Each validator:
|
|||||||
|
|
||||||
1. Creates its tokenizer and runs `tokenize()`
|
1. Creates its tokenizer and runs `tokenize()`
|
||||||
2. Collects tokenizer errors
|
2. Collects tokenizer errors
|
||||||
3. Runs `analyzeTokens()` — language-specific structural analysis
|
3. Runs `analyzeTokens()` - language-specific structural analysis
|
||||||
4. Runs `buildModuleInfo()` — populates `ModuleInfo` with imports, functions, classes
|
4. Runs `buildModuleInfo()` - populates `ModuleInfo` with imports, functions, classes
|
||||||
5. Computes validity, warning/info counts, duration
|
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.
|
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` |
|
| `sourcePath: string = ""` (optional) | `ValidationResult.durationMs: float` |
|
||||||
|
|
||||||
**Guarantees**:
|
**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)
|
- Empty source returns `E0009` (or `valid: true` for YAML/TOML where empty is valid)
|
||||||
- File not found returns `E0011`
|
- File not found returns `E0011`
|
||||||
- Unknown flavor with no detection possible returns an `E0007` error
|
- Unknown flavor with no detection possible returns an `E0007` error
|
||||||
@ -686,8 +686,8 @@ Every validation function follows the same contract:
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `nim_tokenizer.nim` — `NimTokenizer` |
|
| Tokenizer | `nim_tokenizer.nim` - `NimTokenizer` |
|
||||||
| Validator | `nim_validator.nim` — `NimValidator` |
|
| 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.) |
|
| 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 |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -707,8 +707,8 @@ Structural extraction: imports (`from X import Y`), functions (`proc`, `func`, `
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `bash_tokenizer.nim` — `BashTokenizer` |
|
| Tokenizer | `bash_tokenizer.nim` - `BashTokenizer` |
|
||||||
| Validator | `bash_validator.nim` — `BashValidator` |
|
| 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) |
|
| 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 |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -728,8 +728,8 @@ Special features: shebang detection (`#!`), heredoc detection (`<<EOF`), variabl
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `python_tokenizer.nim` — `PythonTokenizer` |
|
| Tokenizer | `python_tokenizer.nim` - `PythonTokenizer` |
|
||||||
| Validator | `python_validator.nim` — `PythonValidator` |
|
| 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) |
|
| 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 |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -749,8 +749,8 @@ Structural extraction: imports (`import X`, `from X import Y`), functions (`def`
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `javascript_tokenizer.nim` — `JavaScriptTokenizer` |
|
| Tokenizer | `javascript_tokenizer.nim` - `JavaScriptTokenizer` |
|
||||||
| Validator | `javascript_validator.nim` — `JavaScriptValidator` |
|
| 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) |
|
| 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 |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -768,9 +768,9 @@ Structural extraction: imports/exports, functions (`function`, `async`), classes
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `php_tokenizer.nim` — `PHPTokenizer` |
|
| Tokenizer | `php_tokenizer.nim` - `PHPTokenizer` |
|
||||||
| Validator | `php_validator.nim` — `PHPValidator` |
|
| Validator | `php_validator.nim` - `PHPValidator` |
|
||||||
| Mode switching | Tracks `inPhpMode` flag — toggles on `<?php`/`<?` and `?>` |
|
| Mode switching | Tracks `inPhpMode` flag - toggles on `<?php`/`<?` and `?>` |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
|------------|-----------|----------|------|
|
|------------|-----------|----------|------|
|
||||||
@ -785,8 +785,8 @@ Structural extraction: functions (`function`, `fn`), classes (`class`), imports/
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `html_tokenizer.nim` — `HTMLTokenizer` |
|
| Tokenizer | `html_tokenizer.nim` - `HTMLTokenizer` |
|
||||||
| Validator | `html_validator.nim` — `HTMLValidator` |
|
| Validator | `html_validator.nim` - `HTMLValidator` |
|
||||||
| Void elements | 14: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr |
|
| Void elements | 14: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -801,8 +801,8 @@ Special features: tag stack balancing, void element awareness (no closing tag ne
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Tokenizer | `jinja_tokenizer.nim` — `JinjaTokenizer` |
|
| Tokenizer | `jinja_tokenizer.nim` - `JinjaTokenizer` |
|
||||||
| Validator | `jinja_validator.nim` — `JinjaValidator` |
|
| Validator | `jinja_validator.nim` - `JinjaValidator` |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
|------------|-----------|----------|------|
|
|------------|-----------|----------|------|
|
||||||
@ -820,7 +820,7 @@ Special features: tracks 12 block tag types (`for`, `if`, `block`, `macro`, `fil
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Validator | `config_validators.nim` — `JSONValidator` |
|
| Validator | `config_validators.nim` - `JSONValidator` |
|
||||||
| Method | Uses Nim's built-in `parseJson()` for full validation |
|
| Method | Uses Nim's built-in `parseJson()` for full validation |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -833,7 +833,7 @@ Special features: tracks 12 block tag types (`for`, `if`, `block`, `macro`, `fil
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Validator | `config_validators.nim` — `YAMLValidator` |
|
| Validator | `config_validators.nim` - `YAMLValidator` |
|
||||||
| Method | Line-by-line structural analysis |
|
| Method | Line-by-line structural analysis |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -849,7 +849,7 @@ Note: Empty YAML (only whitespace/comments) is `valid: true`.
|
|||||||
|
|
||||||
| Aspect | Details |
|
| Aspect | Details |
|
||||||
|--------|---------|
|
|--------|---------|
|
||||||
| Validator | `config_validators.nim` — `TOMLValidator` |
|
| Validator | `config_validators.nim` - `TOMLValidator` |
|
||||||
| Method | Line-by-line structural analysis |
|
| Method | Line-by-line structural analysis |
|
||||||
|
|
||||||
| Error Code | Condition | Severity | Hint |
|
| Error Code | Condition | Severity | Hint |
|
||||||
@ -912,7 +912,7 @@ Supported flavors:
|
|||||||
| 0 | Validation successful (or help/version requested) |
|
| 0 | Validation successful (or help/version requested) |
|
||||||
| Non-zero (Nim default) | Runtime error |
|
| 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
|
### 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
|
- **Never crash**: Every validation path in a try/except
|
||||||
- **No external deps**: Pure Nim standard library
|
- **No external deps**: Pure Nim standard library
|
||||||
- **DRY**: Common logic in `tokenizerbase.nim` and `validatorbase.nim`
|
- **DRY**: Common logic in `tokenizerbase.nim` and `validatorbase.nim`
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
import std/[json, strutils, os, strformat, sequtils, algorithm]
|
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/types
|
||||||
import nimcheck/core/tokenizerbase
|
import nimcheck/core/tokenizerbase
|
||||||
import nimcheck/core/validatorbase
|
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.
|
## Each language validator registers itself here.
|
||||||
## This file is imported by the main nimcheck.nim module.
|
## 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 hintStr = &"Add a closing '{pairStr}' before this point"
|
||||||
let err = newValidationError(
|
let err = newValidationError(
|
||||||
esError,
|
esError,
|
||||||
&"Unclosed '{ch}' — expected matching '{pairStr}'",
|
&"Unclosed '{ch}' - expected matching '{pairStr}'",
|
||||||
ErrUnclosedBracket,
|
ErrUnclosedBracket,
|
||||||
pos,
|
pos,
|
||||||
SourceRange(startPos: pos, endPos: pos),
|
SourceRange(startPos: pos, endPos: pos),
|
||||||
@ -215,12 +215,12 @@ proc validateBrackets*(self: TokenizerBase): seq[ValidationError] =
|
|||||||
self.errors.add(result)
|
self.errors.add(result)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Abstract interface — subclasses must implement
|
# Abstract interface - subclasses must implement
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
method tokenize*(self: TokenizerBase) {.base, gcsafe.} =
|
method tokenize*(self: TokenizerBase) {.base, gcsafe.} =
|
||||||
## Tokenize the entire source. Must be overridden.
|
## 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] =
|
proc getAllTokens*(self: TokenizerBase): seq[Token] =
|
||||||
## Get all tokens after tokenization.
|
## Get all tokens after tokenization.
|
||||||
|
|||||||
@ -36,7 +36,7 @@ proc newValidatorBase*(
|
|||||||
result.result.flavor = options.flavor
|
result.result.flavor = options.flavor
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Template methods — subclasses override
|
# Template methods - subclasses override
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
method createTokenizer*(self: ValidatorBase): TokenizerBase {.base, gcsafe.} =
|
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
|
self.result.moduleInfo.linesOfCode = self.source.count('\n') + 1
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Never crash — always report useful error
|
# Never crash - always report useful error
|
||||||
self.result.valid = false
|
self.result.valid = false
|
||||||
self.result.errors.add(newValidationError(
|
self.result.errors.add(newValidationError(
|
||||||
esCritical,
|
esCritical,
|
||||||
@ -129,7 +129,7 @@ method validate*(self: ValidatorBase): ValidationResult {.base, gcsafe.} =
|
|||||||
"E9999",
|
"E9999",
|
||||||
newSourcePosition(0, 0, 0),
|
newSourcePosition(0, 0, 0),
|
||||||
newSourceRange(newSourcePosition(0, 0, 0), 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):
|
when defined(nimcheckDebug):
|
||||||
if self.options.debugMode:
|
if self.options.debugMode:
|
||||||
@ -162,7 +162,7 @@ proc isValid*(self: ValidatorBase): bool =
|
|||||||
self.result.valid
|
self.result.valid
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Factory — create correct validator for a flavor
|
# Factory - create correct validator for a flavor
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|||||||
@ -77,7 +77,7 @@ method tokenize*(self: PHPTokenizer) =
|
|||||||
self.emitToken(tkNewline, "\n", startPos)
|
self.emitToken(tkNewline, "\n", startPos)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Inside PHP mode — check for exit
|
# Inside PHP mode - check for exit
|
||||||
if self.peekString(2) == "?>" and self.inPhpMode:
|
if self.peekString(2) == "?>" and self.inPhpMode:
|
||||||
for _ in 1..2: discard self.advance()
|
for _ in 1..2: discard self.advance()
|
||||||
self.emitToken(tkDirective, "?>", startPos)
|
self.emitToken(tkDirective, "?>", startPos)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user