Add procedure specifications with (: ...) syntax
Implement Phase 1 of procedure specs — machine-readable predicate-based annotations stored in procedure metadata with zero runtime cost.
The (: ...) form is recognized as the first body expression in define and lambda. If present, it's stripped from the body and stored as metadata under the 'spec key.
Core changes: - Add spec-form?, extract-spec, %set-spec! helpers to (sigil core) - Modify lambda-transformer to detect and extract spec forms - Modify define-transformer to handle specs in function shorthand - Add procedure-spec accessor exported from (sigil core)
New module (sigil spec) with: - spec->string: format spec list as readable string - any?: always-true predicate for "any type" specs - void?: test if value is the undefined/void value
Integration: - REPL ,doc command displays spec line when present - nREPL doc/describe responses include :spec field - MCP tools show spec in lookup and suggest-import output
src/sigil/repl/commands.sgl | 30 ++++++++++++++++++++++--------
1 file changed, 22 insertions(+), 8 deletions(-)src/sigil/repl/commands.sglmodified
(sigil hooks) (sigil ansi) (sigil string) (sigil spec) (sigil docs lookup) (sigil docs search)) (loop (cdr imports))))))) ;; Look up documentation for a symbol in a module ;; Returns (kind signature summary full) or #f ;; Returns (kind signature summary full spec-string) or #f (define (lookup-symbol-doc mod sym) (let ((mod-name (module-name mod)) (bindings (module-bindings mod))) ;; Try home module first, then current module (export-doc (or (and home-mod-name (get-export-details home-mod-name sym)) (get-export-details mod-name sym)))) (get-export-details mod-name sym))) ;; Get spec from runtime value if it's a procedure (spec-str (and val (procedure? val) (spec->string (procedure-spec val))))) (cond ;; Found JSON documentation (export-doc (list kind final-sig (if doc (extract-first-line doc) #f) doc)))) doc spec-str)))) ;; No JSON doc - check if it's a runtime binding we know about ((and val (procedure? val)) (let* ((arity (procedure-arity val)) (list (if arity 'native 'procedure) sig #f #f))) #f spec-str))) ;; Non-procedure binding (val (list 'variable (symbol->string sym) #f #f #f)) ;; Check if it's a syntax (macro) in current module ((memq sym (module-syntaxes mod)) (list 'syntax (format "(~a ...)" sym) #f #f #f)) ;; Check imported modules for syntax (else (if (export-details-description doc) (extract-first-line (export-details-description doc)) #f) (export-details-description doc)) (export-details-description doc) #f) ;; No JSON doc, just show it's a syntax (list 'syntax (format "(~a ...)" sym) #f #f #f))) #f))))))) (else (loop rest (cons line result) #f))))))))) ;; Print documentation nicely (uses 4-element format) ;; Print documentation nicely (uses 5-element format) (define (print-doc sym info) (let ((kind (car info)) (sig (cadr info)) ;; summary is caddr, full doc is cadddr (doc (cadddr info))) ;; summary is caddr, full doc is cadddr, spec-str is 5th (doc (cadddr info)) (spec-str (and (> (length info) 4) (list-ref info 4)))) (display sig) (display " -> ") (display kind) (newline) (when spec-str (display (a:dim (format " Spec: ~a" spec-str))) (newline)) (if doc (let ((formatted (format-docstring doc))) (newline)