Files
gulie/README.md
2026-04-02 00:26:05 +02:00

216 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gulie the GUile LInt Exorcist
A linter, static analyser, and formatter for [Guile Scheme](https://www.gnu.org/software/guile/).
```
$ gulie gulie/
gulie/engine.scm:97:80: warning: line-length: line exceeds 80 characters (82)
gulie/tokenizer.scm:131:0: warning: trailing-whitespace: trailing whitespace
```
## Why
No linter, formatter, or static analysis tool exists for Guile Scheme. gulie
fills that gap with a two-pass architecture that catches both surface-level
formatting issues and deep semantic problems.
## Features
- **Surface rules** (no parsing needed): trailing whitespace, line length, tabs,
excessive blank lines, comment style conventions
- **Semantic rules** (via Guile's compiler): unused variables, unbound
variables, arity mismatches, format string errors, shadowed top-levels,
unused modules
- **Inline suppression**: `; gulie:suppress rule-name` on a line, or region
disable/enable blocks
- **Auto-fix mode**: `--fix` applies automatic corrections where available
- **Configuration**: `.gulie.sexp` in your project root, overridable via CLI
- **CI friendly**: exit code 0 for clean, 1 for findings
## Requirements
- [Guile](https://www.gnu.org/software/guile/) 3.0 or later
## Installation
Clone the repository and ensure `bin/gulie` is on your `PATH`, or run it
directly:
```sh
bin/gulie --check .
```
## Usage
```
gulie [OPTIONS] [FILE|DIR...]
Options:
-h, --help Show help message
-v, --version Print version
--check Check mode (default): report issues, exit non-zero on findings
--fix Fix mode: auto-fix what's possible, report the rest
--init Generate .gulie.sexp template in current directory
--pass PASS Run only: surface, semantic, all (default: all)
--config FILE Config file path (default: auto-discover .gulie.sexp)
--rule RULE Enable only this rule
--disable RULE Disable this rule
--severity SEV Minimum severity: error, warning, info
--output FORMAT Output format: standard (default), json, compact
--list-rules List all available rules
```
### Examples
Check a single file:
```sh
gulie mylib.scm
```
Check an entire project:
```sh
gulie src/
```
Auto-fix trailing whitespace and other fixable issues:
```sh
gulie --fix src/
```
Generate a config template:
```sh
gulie --init
```
## Configuration
gulie looks for `.gulie.sexp` in the current directory and parent directories.
Generate a template with `gulie --init`.
```scheme
((line-length . 80)
(indent . 2)
(max-blank-lines . 2)
(enable trailing-whitespace line-length no-tabs blank-lines
comment-semicolons unused-variable unbound-variable
arity-mismatch)
(disable)
(rules
(line-length (max . 100)))
(indent-rules
(define . 1) (let . 1) (lambda . 1)
(with-syntax . 1) (match . 1))
(ignore "build/**" ".direnv/**"))
```
## Inline Suppression
Suppress a rule on the current line:
```scheme
(define x "messy") ; gulie:suppress trailing-whitespace
```
Suppress all rules on the next line:
```scheme
;; gulie:suppress
(define intentionally-long-variable-name "value")
```
Region disable/enable:
```scheme
;; gulie:disable line-length
(define long-line ...............................................)
(define another .................................................)
;; gulie:enable line-length
```
## Architecture
gulie uses a two-pass design:
```
.gulie.sexp
|
file.scm --+--> [Tokenizer] --> tokens --> [CST parser] --> CST
| |
| [Pass 1: Surface] line rules + CST rules
| |
| diagnostics-1
|
+--> [Guile compiler] --> Tree-IL --> CPS
|
[Pass 2: Semantic] Guile's built-in analyses
|
diagnostics-2
|
[merge + suppress + sort + report]
```
**Pass 1** uses a hand-written tokenizer that preserves all whitespace, comments,
and exact source text. The critical invariant:
`(string-concatenate (map token-text (tokenize input)))` reproduces the input
exactly. This feeds a lightweight concrete syntax tree for formatting checks.
**Pass 2** delegates to Guile's own compiler and analysis infrastructure:
`unused-variable-analysis`, `arity-analysis`, `format-analysis`, and others.
These are battle-tested and handle macroexpansion correctly.
The two passes are independent because Guile's reader irrecoverably strips
comments and whitespace — there is no way to get formatting info and semantic
info from a single parse.
## Rules
| Rule | Type | Category | Description |
|------|------|----------|-------------|
| `trailing-whitespace` | line | format | Trailing spaces or tabs |
| `line-length` | line | format | Line exceeds maximum width |
| `no-tabs` | line | format | Tab characters in source |
| `blank-lines` | line | format | Excessive consecutive blank lines |
| `comment-semicolons` | cst | style | Comment style conventions (`;`/`;;`/`;;;`) |
| `unused-variable` | semantic | correctness | Unused local variable |
| `unused-toplevel` | semantic | correctness | Unused top-level definition |
| `unused-module` | semantic | correctness | Unused module import |
| `unbound-variable` | semantic | correctness | Reference to undefined variable |
| `arity-mismatch` | semantic | correctness | Wrong number of arguments |
| `shadowed-toplevel` | semantic | correctness | Top-level binding shadows import |
| `format-string` | semantic | correctness | Format string validation |
## Module Structure
```
gulie/
cli.scm Command-line interface
config.scm Configuration loading and merging
diagnostic.scm Diagnostic record type and formatting
tokenizer.scm Hand-written lexer preserving all tokens
cst.scm Token stream to concrete syntax tree
compiler.scm Guile compiler wrapper for semantic analysis
rule.scm Rule record type and registry
engine.scm Orchestrator: file discovery, pass sequencing
suppression.scm Inline suppression parsing and filtering
rules/
surface.scm Line-based formatting rules
comments.scm Comment style rules
```
## Testing
```sh
guile --no-auto-compile -L . -s test/run-tests.scm
```
84 tests covering tokenizer roundtrip, CST parsing, surface rules, suppression,
and semantic analysis.
## Licence
[TODO: add licence]