8000 GitHub - Mnehmos/mnehmos.nls.lang: Natural Language Source - NLS is the missing middle layer between AI Conversations and Production Code: Structured English that compiles deterministically to Python · GitHub
[go: up one dir, main page]

Skip to content

About

Natural Language Source - NLS is the missing middle layer between AI Conversations and Production Code: Structured English that compiles deterministically to Python

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Natural Language Source (NLS)

PyPI PyPI downloads Tests CI Python License Docs

The source is natural language. The compiled artifact can be Python or TypeScript.

NLS is a programming language where specifications are written in natural language that anyone can read—managers, auditors, domain experts—not just programmers. The nlsc compiler translates .nl files into executable Python or TypeScript with type hints, validation, and documentation.

Installation

# Install from PyPI
pip install nlsc

# With tree-sitter parser (faster, better error recovery)
pip install "nlsc[treesitter]"

# For development (from source)
git clone https://github.com/Mnehmos/mnehmos.nls.lang.git
cd mnehmos.nls.lang
pip install -e ".[dev,treesitter]"

The distribution contract (PyPI-first, Trusted Publishing, and the machine-readable workspace manifest used by tooling dashboards) lives in .mnehmos/tool.json; see docs/distribution.md for the workspace workflow and release flow.

Windows File Association

Get custom icons for .nl files in Windows Explorer:

# Download and run the installer from GitHub Releases
# Or use the CLI:
nlsc assoc --user   # Current user (no admin)
nlsc assoc          # System-wide (requires admin)

Then right-click any .nl file → Open with → select the NLS launcher and check "Always use this app".

For macOS and Linux (MIME type, desktop actions, context menus), see the OS integration guide; nlsc assoc --desktop generates the Linux integration files from any platform.

Quick Start

15-Minute Quickstart — build a real shipping-quote calculator with types, guards, total branches, tests, both targets, and a CI gate. Every command in the guide is exercised by the test suite.

The short version:

# Initialize a new project (basic scaffold, library, service, or cli template)
nlsc init m
8000
y-project
nlsc init my-lib --template library
nlsc init --list-templates
cd my-project

# Create your first .nl file
cat > src/calculator.nl << 'EOF'
@module calculator
@target python

[add]
PURPOSE: Add two numbers together
INPUTS:
  - a: number
  - b: number
RETURNS: a + b

[divide]
PURPOSE: Divide two numbers safely
INPUTS:
  - numerator: number
  - divisor: number
GUARDS:
  - divisor must not be zero -> ValueError("Cannot divide by zero")
RETURNS: numerator / divisor

@test [add] {
  add(2, 3) == 5
  add(-1, 1) == 0
}
EOF

# Compile to Python
nlsc compile src/calculator.nl

# Compile the same spec to TypeScript
nlsc compile src/calculator.nl -t typescript

# Run the tests
nlsc test src/calculator.nl

Example

Input: math.nl

@module math
@version 1.0.0
@target python

@type Point {
  x: number
  y: number
}

[distance]
PURPOSE: Calculate distance between two points
INPUTS:
  - p1: Point
  - p2: Point
LOGIC:
  1. dx = p2.x - p1.x
  2. dy = p2.y - p1.y
  3. squared = dx * dx + dy * dy
RETURNS: sqrt(squared)
DEPENDS: [sqrt]

@test [distance] {
  distance(Point(0, 0), Point(3, 4)) == 5.0
}

Output: math.py

"""math module - Generated by nlsc"""
from dataclasses import dataclass
from math import sqrt

@dataclass
class Point:
    """Point type"""
    x: float
    y: float

def distance(p1: Point, p2: Point) -> float:
    """Calculate distance between two points"""
    dx = p2.x - p1.x
    dy = p2.y - p1.y
    squared = dx * dx + dy * dy
    return sqrt(squared)

CLI Reference

Command Description
nlsc init <path> Initialize new NLS project
nlsc compile <file> Compile .nl to Python or TypeScript
nlsc run <file> Compile and execute a module's @main
nlsc install [path] Resolve package dependencies and write nls.pkg.lock
nlsc verify <file> Validate syntax and dependencies
nlsc test <file> Run @test specifications
nlsc ir <file> Emit target-neutral IR (--check validates it backend-free)
nlsc lint <file|dir> Intent-quality lint rules (--strict for CI)
nlsc fmt <file|dir> Canonical formatting (--check/--diff for CI)
nlsc ci <file> CI gate: strict semantics + frozen lockfile
nlsc provenance <file> Record/review edit provenance (LLM vs human)
nlsc graph <file> Generate dependency diagrams
nlsc diff <file> Show changes since last compile
nlsc watch <dir> Continuous compilation on file changes
nlsc atomize <file.py> Extract ANLUs from existing Python
nlsc assoc Install Windows file association
nlsc lsp Start the NLS language server
nlsc explain <CODE> Extended explanation for any diagnostic
nlsc lock:check / lock:update Inspect or refresh a .nl.lock

nlsc lsp --json emits structured startup diagnostics for missing optional LSP dependencies (ELSP001) and server-start failures (ELSP002).

Strict vs. scaffold compilation (Issue #190)

By default, LOGIC steps that describe work in prose still compile: the artifact is produced, the step becomes a visible TODO placeholder, and the module docstring is marked Status: INCOMPLETE SCAFFOLD so it can never be mistaken for a verified implementation. nlsc compile/verify print EIR002/EIR004 warnings for each unresolved step.

Add --strict to compile, verify, run, test, or watch to reject unresolved executable content up front — no runnable artifact, no invented return values (RETURNS: number alone is a declared type, not a zero), and prose edge cases mixed with executable conditions are refused.

Compilation is also type-checked before any backend runs (Issue #195). Calls resolve through a semantic symbol table — module ANLUs (kebab and snake spellings), declared @type records, documented builtins, and @imports — so unknown [anlu] calls, undefined value uses, unknown fields, duplicate or colliding identifiers (foo-bar vs foo_bar), and arity mismatches fail verification in every mode (ESEM001–ESEM007). Argument-type mismatches, DEPENDS drift, and undeclared foreign calls are strict-mode failures (ESEM003/ESEM008/ESEM009), and inferred call dependencies come from the implementation, not from whether you remembered DEPENDS:

nlsc compile src/payment.nl --strict   # fails until every step is executable
nlsc ir src/payment.nl --strict        # same contract at the IR boundary

Compiling to a target that cannot represent your content fails explicitly (no silent drops): @literal and @main are Python-target features, and nlsc compile -t typescript refuses them with ETARGET002 rather than emitting a program that calls undefined functions.

Cross-target conformance (Issue #202): the TypeScript backend emits a small NLS runtime (structural equality, NLS truthiness, Python-compatible division/modulo) and translates expressions through the target-neutral IR, so values, falsy empty lists, ZeroDivisionError, and guard error identities behave identically under Python and Node. The semantic test suite runs against both targets in CI, including tsc --strict validation of every fixture.

Global Options

--parser {auto,regex,treesitter}  # Parser backend (default: auto)
--version                     # Show version
--help                        # Show help

Examples

# Compile with tree-sitter parser
nlsc --parser treesitter compile src/auth.nl

# Compile to TypeScript
nlsc compile src/auth.nl -t typescript

# Generate Mermaid dependency diagram
nlsc graph src/order.nl --format mermaid

# Visualize dataflow for specific function
nlsc graph src/order.nl --anlu process-order --dataflow
# Execution paths: source order, effect-only steps, labeled branch edges
nlsc graph src/order.nl --anlu process-order --control
# Dataflow diagrams show dependency layers labeled with checked parallel
# eligibility: a layer is 'parallel-eligible' only when effect, alias,
# and failure constraints certify its steps independent (Issue #199).

# Watch directory and run tests on changes
nlsc watch src/ --test

# Show what changed since last compile
nlsc diff src/api.nl --full

CI gate: nlsc ci (Issue #149)

nlsc ci is the automation-native front door: non-interactive, strict by default (any scaffold content, contract drift, or fatal semantic error fails), and requires a current lockfile that it never rewrites. Optional stages compile with reproducibility verification (the regenerated output must reproduce the lockfile's recorded target hash) and run @test blocks:

nlsc ci src/app.nl                  # strict gate + lockfile freshness
nlsc ci src/app.nl --compile        # + compile & verify reproducible hash
nlsc ci src/app.nl --compile --test # + run @test specifications
nlsc ci src/app.nl --json           # structured results for CI tooling
nlsc compile src/app.nl --frozen-lockfile  # plain compile, lock never rewritten

Exit codes are stable: 0 success, 1 any diagnostic (parse, strict gate, stale/missing/tampered lockfile, failing tests). Run nlsc compile once and commit the .nl.lock file — nlsc ci requires it, npm-ci style.

# .github/workflows/nls.yml
name: NLS CI
on: [push, pull_request]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: '3.12' }
      - run: pip install nlsc
      - run: nlsc ci src/app.nl --compile --test

GitHub Action

Use the NLS Compiler Action in your CI/CD pipelines for zero-config validation:

# .github/workflows/nls.yml
name: NLS Validation

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: Mnehmos/mnehmos.nls.lang/action@master
        with:
          verify: 'true'       # Verify all .nl files parse correctly
          compile: 'false'     # Compile .nl files to Python
          test: 'false'        # Run @test blocks
          lock-check: 'false'  # Verify lockfiles are current
          path: '.'            # Path to search for .nl files

Action Inputs

Input Default Description
verify true Verify all .nl files parse correctly
compile false Compile .nl files to target language
test false Run @test blocks from .nl files
lock-check false Check that lockfiles are up to date
path . Path to search for .nl files
python-version 3.12 Python version to use
fail-on-warning false Fail the action if there are warnings

Action Outputs

Output Description
verified-files Number of verified .nl files
compiled-files List of compiled output files
test-results Test pass/fail summary
warnings Number of warnings

Language Features

Security

.nl files are code: verify/ir/graph/compile never execute them, while run/test do. For untrusted input:

nlsc run untrusted.nl --sandbox --timeout 5

--sandbox blocks process execution, networking, native interop, and writes outside the run directory in an isolated interpreter — defense in depth, not a security boundary (use containers/VMs for hostile input). Trust levels, the sandbox's honest limits, the threat model, and the responsible-disclosure policy live in docs/security.md.

Versioning

Declare the language-spec revision your file targets and get compatibility checks for free:

@nls 0.1     # spec revision (optional; see docs/versioning.md)
@version 1.0.0

Same major is compatible, a newer minor warns under --strict (EVER002), and a different major fails everywhere (EVER001). Spec changes and toolchain changes are tracked separately in the CHANGELOG.

Performance

Compile time is ~1 ms per ANLU plus interpreter startup; 2000-ANLU modules compile in ~4 s with linear scaling. Measured tables, limits, and known bottlenecks live in docs/performance.md; reproduce them with python benchmarks/run_benchmarks.py, which CI also runs and tracks as an artifact.

Feature Examples

A strictly-verified example per language feature lives in examples/features/ — guards, total branches, types and invariants, recursion, composition, literals, imports, FSM markers, properties, and collections. The whole corpus is a regression suite (tests/test_issue_142_examples.py) and is catalogued in the examples gallery.

ANLU Blocks (Functions)

Branches are explicit and total when they produce a value:

IF flag THEN 1 -> x ELSE 0 -> x

A binding that exists on only one path is rejected before either backend runs — no silent UnboundLocalError/ReferenceError — and rebound names fail --strict because checked bindings are immutable.

[function-name]
PURPOSE: What this function does
INPUTS:
  - param1: type
  - param2: type, optional
GUARDS:
  - validation condition -> ErrorType("message")
LOGIC:
  1. step description -> variable
  2. another step
EDGE CASES:
  - condition -> behavior
RETURNS: expression
DEPENDS: [other-function], [another]

Type Definitions

@type Order {
  id: string, required
  items: list of OrderItem
  total: number, "Order total in cents"
  status: string
}

@type OrderItem extends BaseItem {
  quantity: number
  price: number
}

Test Specifications

@test [add] {
  add(1, 2) == 3
  add(0, 0) == 0
  add(-5, 5) == 0
}

Property-Based Testing

@property [add] {
  add(a, b) == add(b, a)           # commutativity
  add(a, 0) == a                   # identity
  forall x: number -> add(x, -x) == 0
}

Type Invariants

@type Account {
  balance: number
  owner: string
}

@invariant Account {
  balance >= 0
  len(owner) > 0
}

Directives

@module name           # Module name
@version 1.0.0         # Semantic version
@target python         # Target language
@imports other_module  # Import dependencies
@use math.core         # Stdlib domain dependency (Issue #90)

The Philosophy

"The conversation is the programming. The .nl file is the receipt. The code is the artifact."

  • .nl files — Human-readable specifications anyone can review
  • .py files — Compiled artifacts (like assembly from C)
  • .nl.lock files — Deterministic hashes for reproducible builds

Why NLS?

  1. Readable by everyone — Non-programmers can review business logic
  2. Auditable — Clear mapping from intent to implementation
  3. Testable — Specifications include test cases
  4. Versionable — Lock files ensure reproducibility
  5. Toolable — Tree-sitter grammar enables IDE support

Project Status

Component Status
Both parsers (regex + tree-sitter) sharing one canonical lowering ✅ Complete
Target-neutral IR with canonical text/JSON (nlsc ir) ✅ Complete
Checked pipeline: types, control flow, failures, effects, typestate, retry policies ✅ Complete
Python emitter (IR-driven) ✅ Complete
TypeScript emitter (IR-driven, tsc --strict conformance in CI) ✅ Complete
Cross-target semantic conformance suite (Python + Node) ✅ Complete
Guard validation, edge cases, type invariants, property tests ✅ Complete
Semantic lock identity + --frozen-lockfile reproducibility ✅ Complete
Capability matrix + per-target emitter semantics markers ✅ Complete
Quality gates: nlsc ci, lint, fmt, provenance ✅ Complete
Local package model (nls.pkg.json, nlsc install) ✅ Complete
Emitter plugin registry (nlsc.targets entry points) ✅ Complete
Watch mode, LSP server, GitHub Action, PyPI distribution, VS Code extension, Windows installer ✅ Complete
Retry/timeout runtime emission ◐ Checked, refused by both targets until emitted
Typed effect handlers (#207/#208), Rust target (#24) ◐ Planned — staged in the issues

1,187 tests passing across three operating systems and two Python versions, including a cross-target conformance suite that executes compiled modules on both backends. See GitHub Issues for the roadmap.

Documentation

📚 Full Documentation — Hosted on GitHub Pages

Contributing

# Clone and install
git clone https://github.com/Mnehmos/mnehmos.nls.lang.git
cd mnehmos.nls.lang
pip install -e ".[dev,treesitter]"

# Run tests
pytest tests/ -v

# Run tree-sitter grammar tests
cd tree-sitter-nl && npx tree-sitter test

License

MIT

About

Natural Language Source - NLS is the missing middle layer between AI Conversations and Production Code: Structured English that compiles deterministically to Python

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0