Skip to main content

Nema Language

Experimental agent-oriented language for compiling NeuroState into control flow.

"Agents don't just compute. They feel."

PyPI version Python 3.10+ License: MIT Live Demo

Nema REPL demo


Browser Demo (WebAssembly)

Open browser_demo/index.html directly in any modern browser — no server needed.

The demo compiles two agents (Emilia and Kernel) from .nema source to WASM. Each slider writes directly to a WASM f64 global; gate conditions are re-evaluated in real time. Drag a field below its threshold and watch the gate flip from ✅ to ❌ instantly.

Emilia_explore()    requires dp > 0.6     → dp + ac
Emilia_rest()       requires s > 0.5 ∧ gaba > 0.4  → s + gaba
Emilia_connect()    requires ox > 0.6     → ox + s
Emilia_mood_score() (no gate)             → Σ all fields

To regenerate after editing demo.nema:

python3 nema.py browser_demo/demo.nema --wasm
python3 browser_demo/gen.py

What is Nema?

Nema is a research prototype language where every agent carries a NeuroState — a 6-dimensional affective state based on neurotransmitters (dopamine, serotonin, acetylcholine, oxytocin, GABA, endorphin). Emotional state gates function execution, propagates between agents, decays over time, and drives memory management.

@requires(dp > 0.6) compiles directly to fcmp ogt + conditional branch in LLVM IR — not a runtime flag, not a config value, machine code.

One of the first experimental languages to compile agent affective state (NeuroState) into executable control flow via LLVM IR.


Install

pip install nema-lang

Requires Python 3.10+ and LLVM (via llvmlite).


Quick Start

# Run the REPL interpreter
nema hello.nema

# JIT compile and run benchmarks
python -m jit_run hello.nema

# Generate LLVM IR
nema hello.nema --compile
# → produces hello.ll

# Compile to WebAssembly (WAT + WASM binary via wabt)
nema hello.nema --wasm
# → produces hello.wat + hello.wasm

Language Example

agent Neko {
  mood: NeuroState = {
    dp: 0.8, s: 0.5, ac: 0.7,
    ox: 0.6, gaba: 0.4, e: 0.6
  }

  // compiles to: fcmp ogt double %dp, 6.000000e-01
  @requires(dp > 0.6)
  fn explore(path) { }

  // multi-condition gate — both must be true
  @requires(gaba > 0.5 and s > 0.4)
  fn sleep() { }
}

agent Kernel {
  mood: NeuroState = {
    dp: 0.5, s: 0.7, ac: 0.9,
    ox: 0.4, gaba: 0.5, e: 0.5
  }

  // real malloc — gated by emotional focus
  @requires(ac > 0.8 and dp > 0.3)
  fn alloc(size: i64) -> ptr<i64> { }

  @requires(ac > 0.5)
  fn write(addr: ptr<i64>, val: i64) -> void { }

  @requires(dp > 0.3)
  fn read(addr: ptr<i64>) -> i64 { }

  fn free(addr: ptr<i64>) -> void { }
}

Architecture

Nema source (.nema)
       ↓
   Lexer / Parser
       ↓
   Type Checker  ← validates NeuroState fields, ranges, always-fail gates
       ↓
      AST
       ↙        ↘
 Interpreter    LLVM Compiler
 (REPL mode)   (JIT / .ll output)
       ↓              ↓
  Runtime         Machine code
(emotion lives)  (emotion compiled)

The 6 Dimensions

Symbol Neurotransmitter Meaning
dp Dopamine Curiosity, motivation
s Serotonin Stability, calm
ac Acetylcholine Focus, attention
ox Oxytocin Trust, empathy
gaba GABA Inhibition, composure
e Endorphin Joy, achievement

REPL Commands

Command Description
show <agent> Display NeuroState + memory
trace <agent> [n] Show last n emotion-change events as a table (rich, default 5)
call <agent> <fn> [args] Call function (emotion-gated)
attract <A> <B> [strength] Set symmetric attraction between agents
remember <agent> <key> <value> Write to working memory
recall <agent> <key> Retrieve from memory
introspect <agent> Verbalize emotional state in Japanese
empathize <A> <B> A absorbs 30% of B's emotional state
log <agent> <msg> Log with emotional context
rand_mood <agent> Randomize NeuroState
summarize <agent> Swap working memory to long-term storage
spawn <agent> <fn> Run agent function in background thread
threads Show all active threads
transfer <from> <to> <var> Transfer ownership between agents
shii <agent> Inject しーちゃん spirit.db → NeuroState

Core Features

Emotion Gates → Machine Code

@requires(dp > 0.6)
fn explore(path) { }

@requires(ac > 0.8 and dp > 0.3)
fn alloc(size: i64) -> ptr<i64> { }

Each condition compiles to fcmp ogt + and i1 + conditional branch in LLVM IR.
Gate-rejected functions return -1 (or null for pointer types).

Real Memory Operations

alloc, write, read, free compile to actual malloc/store/load/free instructions — not simulated, real machine code guarded by emotional state.

Static Type System

fn alloc(size: i64) -> ptr<i64> { }
fn write(addr: ptr<i64>, val: i64) -> void { }

Supported types: i64, i32, f64, bool, void, ptr<T>, NeuroState

Static Type Checker

Validates at parse time:

  • Unknown NeuroState fields → error
  • Values outside [0.0, 1.0] → error
  • @requires(dp > 1.5) → warning (always fails)

Emotional Decay (background thread)

Every 5 seconds, all emotions decay at neurotransmitter-specific rates. Serotonin fades slowly; dopamine faster. Agents grow tired if left alone.

Agent Attraction

attract Neko Shii 0.5

Symmetric emotional pull — agents converge toward each other's state on every tick.
Compiles to (B[f] - A[f]) * strength * 0.1 delta applied symmetrically via LLVM fsub/fmul/fadd.

CPOS Memory Layer

Working memory (RAM, max 5 entries) + long-term storage (JSON).
When gaba ≥ 0.7, composure triggers automatic memory consolidation (swap to disk).

For Loops

for i in 0..5 { log(i) }             // range loop
for val in [1.0, 2.0, 3.0] { log(val) } // list loop

@requires(dp > 0.5)
fn gated_loop() -> f64 {
  for i in 0..3 { log(i) }  // gate rejects entire loop if dp ≤ 0.5
  return dp
}

Result<T> Error Type

fn divide(a: f64, b: f64) -> f64 {
  branch b == 0.0 {
    let r = err("divide by zero")
    log(r)
    return 0.0
  } else {
    let r = ok(a)
    log(r)
    return a
  }
}

fn try_it() -> f64 {
  let result = ok(42.0)
  match result {
    ok(v)    { return v }
    err(msg) { log(msg); return 0.0 }
  }
  return 0.0
}

Typed Channels

let ch = channel<i64>
spawn worker(ch)
send ch 42
recv ch -> val { log(val) }
close ch

set<T> Type (v0.6.0)

let tags = set<i64>
tags.add(42)
tags.add(42)          // duplicates silently ignored
print(tags.size())    // 1
print(tags.contains(42))  // True
tags.remove(42)
let items = tags.to_list()

Supported methods: .add(v), .remove(v), .contains(v) -> bool, .size() -> i64, .to_list() -> list<T>

map<K,V> Type (v0.7.0)

let scores = map<i64, i64>
scores.set(10, 100)
scores.set(20, 200)
print(scores.get(10))       // 100
print(scores.contains(20))  // True
scores.remove(20)
print(scores.keys())        // [10]
print(scores.values())      // [100]

Supported methods: .set(k, v), .get(k), .remove(k), .contains(k) -> bool, .size() -> i64, .keys() -> list<K>, .values() -> list<V>

Emotion Trace Visualization (v0.7.0)

> trace Neko
              Neko
┏━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━┓
┃ 発生源          ┃ field ┃     Δ ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━┩
│ @after:explore │ dp    │  +0.1 │
│ @after:explore │ e     │ +0.05 │
└────────────────┴───────┴───────┘

Renders the same change history as mood.origin(n) (used inside .nema code) as a rich table in the REPL, color-coded by sign (green=increase, red=decrease). Falls back to the plain-text format if rich isn't installed.

Standard Library (v0.6.0)

import "lib/math.nema"
import "lib/collections.nema"

agent Main {
  fn run() -> void {
    let m = Math()
    print(m.abs(0 - 5))         // 5
    print(m.pow(2, 8))          // 256
    print(m.clamp(15, 0, 10))   // 10

    let c = Collections()
    let nums = [1, 2, 3, 2, 1]
    c.unique(nums)              // unique count: 4
  }
}

lib/math.nema: abs, max, min, clamp, pow
lib/collections.nema: unique, intersection_size
lib/io.nema: print_list, print_hr, print_labeled
lib/sensors.nema: sample (capability-gated read, demonstrates cross-agent import)

Contracts (Layer 9)

agent Counter {
  contract { dp >= 0.1  gaba >= 0.1 }  // checked after every call/tick
}

Violations raise a ContractError and are logged with the offending field.

JIT Performance

Operation Speedup over interpreter
tick (decay) 10.4×
gate check 6.3×
attract 9.6×

LLVM IR Output

; NeuroState as double[6]
@"mood_Neko" = internal global [6 x double] [
  double 0x3fe999999999999a,  ; dp = 0.8
  double 0x3fe0000000000000,  ; s  = 0.5
  ...
]

; @requires(dp > 0.6) → fcmp ogt
define i32 @"fn_Neko_explore"() {
entry:
  %val_dp = load double, double* %dp_ptr
  %cmp_dp = fcmp ogt double %val_dp, 6.000000e-01
  br i1 %gate, label %exec, label %reject
exec:
  ret i32 0
reject:
  ret i32 -1
}

; alloc: real malloc gated by emotion
define i64* @"impl_Kernel_alloc"(i64 %size) {
entry:
  %cmp_ac = fcmp ogt double %ac, 8.000000e-01
  %cmp_dp = fcmp ogt double %dp, 3.000000e-01
  %gate = and i1 %cmp_ac, %cmp_dp
  br i1 %gate, label %exec, label %reject
exec:
  %nbytes = mul i64 %size, 8
  %raw = call i8* @malloc(i64 %nbytes)
  %ptr = bitcast i8* %raw to i64*
  ret i64* %ptr
reject:
  ret i64* null
}

Static Type Checking

nema myfile.nema --check

Catches errors at parse time, before any execution:

[WARN]  Neko.explore: @requires(dp > 1.5) — always fails (dp max is 1.0)
[ERROR] Kernel.alloc: unknown NeuroState field 'motivation' (use: dp s ac ox gaba e)
[ERROR] Kernel.alloc: NeuroState value 1.8 out of range [0.0, 1.0]

Exit code 0 = clean, 1 = errors found.


Safety Model

Nema has six layers of execution safety. No single layer is sufficient — they compose.

Layer 1:  Emotion Gate       @requires(dp > 0.6)  → fcmp ogt in LLVM IR
Layer 2:  Post-condition     @ensures(gaba > 0.3) → verified after execution; runs @on_error on fail
Layer 3:  Fallback           @on_error { ... }    → runs on gate fail OR ensures fail
Layer 4:  Static Type Check  unknown fields / out-of-range values → compile-time error
Layer 5:  Ownership          own / release / recv — double-free raises serotonin penalty
Layer 6:  Capability         capability: { alloc, emit } — privileged ops (alloc/free) require declaration
Layer 7:  Trust Score        trust: { AgentB: 0.8 } — query/send blocked if trust < 0.3
Layer 8:  Memory Isolation   CPOS working memory (max 5) / long-term JSON / auto-swap
Layer 9:  Contract           contract { dp >= 0.1 } — invariant checked after every call/tick
Layer 10: CPOS Gate          @cpos_gate — blocks on NeuroState WARNING or suspicious call pattern

Emotion gates express agent readiness, not permissions. Capabilities enforce permissions. Trust enforces identity. All three compose.

CPOS Gate (Layer 10)

agent SecureAgent {
  mood: NeuroState = { dp: 0.5, s: 0.6, ac: 0.5, ox: 0.5, gaba: 0.3, e: 0.5 }

  @cpos_gate
  fn export_data() {
    let src = mood.origin()   // trace what caused the current mood state
    log(src)
    emit "data exported"
  }
}

@cpos_gate triggers when either condition is met:

  • gaba >= 0.6 — NeuroState WARNING level (accumulated context poisoning)
  • Alternating high/low impact call pattern detected over the last 6 calls (S6-class attack)

mood.origin() returns the trace of what changed the mood state and from where:

[mood.origin]
  ← user_input:hello: gaba+0.15, e+0.1
  ← fn:do_work: s-0.1
  ← user_input:suspicious: gaba+0.12, e+0.05

This closes the gap that scalar gates (C4) and trajectory gates (C5) both leave open.

agent Kernel {
  capability: { alloc, free, write, read, emit }
  trust: { Process: 0.8 }

  @requires(ac > 0.8)      // Layer 1: must be focused
  @ensures(gaba > 0.3)     // Layer 2: must remain calm after
  @on_error { emit kernel_fail 1 }  // Layer 3: fallback if either fails
  fn alloc_buf(size: i64) -> ptr<i64> {
    own buf = alloc(size)  // Layer 5+6: owned + kernel-only
    return buf
  }
}

Examples

File Demonstrates
hello.nema Emotion gates, when blocks, agent attraction
memory.nema CPOS working / long-term memory, gaba-triggered swap
kernel.nema Emotion-gated malloc / write / read / free, ownership transfer
concurrent.nema Multi-agent concurrency, mailbox recv, spawn
for_demo.nema for loops (range & list), emotion-gated iteration
result_demo.nema Result<T> error type, ok(v) / err(msg), match
channel_demo.nema Typed channel<T>, send / recv / close across agents
contract_demo.nema Layer 9 contracts — NeuroState invariants
set_demo.nema set<T> type — .add/.remove/.contains/.size/.to_list
map_demo.nema map<K,V> type — .set/.get/.remove/.contains/.size/.keys/.values
stdlib_demo.nema Standard library — Math, Collections
match_demo.nema match expressions, multi-file import
typed.nema Typed function signatures, ptr<T>

Testing

pip install -e ".[dev]"
pytest

tests/test_demos.py type-checks and runs every .nema file under repo root against regressions (no crashes, no --check errors), plus targeted assertions for set<T>, map<K,V>, and capability-gated alloc.


Files

File Role
lexer.py Tokenizer
parser.py AST parser
ast_nodes.py AST node definitions
typechecker.py Static type checker
evaluator.py Interpreter runtime + concurrent execution
stdlib.py Standard library (introspect, empathize, CPOS)
compiler.py LLVM IR code generator
jit_run.py JIT compiler + runner
nema.py Entry point + REPL
benchmark.py JIT vs interpreter benchmarks
shiichan.py しーちゃん spirit.db → NeuroState bridge

Background

Nema is built on two original concepts:


Nema — where code has feelings.

Release files for nema-lang 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nema-lang 0.7.0
File Size Uploaded
nema_lang-0.7.0.tar.gz 584.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for nema-lang 0.7.0
File Interpreter ABI Platform
nema_lang-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 598.3 kB

Release files / nema_lang-0.7.0.tar.gz

Download URL nema_lang-0.7.0.tar.gz
Size 584.8 kB
Tags Source
SHA-256 checksum
How to use checksums
a7410597f75456b72b74795ac09531c11993a3be69e46a0de1259a8af96c8b7a
BLAKE2b-256 checksum
How to use checksums
a9901f6db4a81bb9e30831448558c81ff95a06ae14e1a3a822f6e44669050a28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.31.0

Release files / nema_lang-0.7.0-py3-none-any.whl

Download URL nema_lang-0.7.0-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b70fa050a1bdad03086f8cdeaf91d0be28cb7645b7d787c7a8fe251e09e3af4
BLAKE2b-256 checksum
How to use checksums
4a4249a4f24df5222d98250c8699c524ea711eb93fb0b411dd99cc93113ee297
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.31.0

Release history Release notifications | RSS feed

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page