Nelumbo documentation
June 23, 2026 · View on GitHub
Nelumbo is a declarative logic meta-language. You define the syntax and semantics of your language in a .nl file, and the same file runs and tests it. Because Nelumbo reasons about facts and falsehoods as peers, it has genuine logical negation — not Prolog's negation-as-failure — and produces results as a two-sided [facts][falsehoods] structure.
Status: Nelumbo is at an early stage of development. Some features are still evolving; where that matters, individual pages note it.
This folder contains the user documentation. For the project overview, build instructions, and release notes, see the repository README. For the presentation-deck version of this material, see NELUMBO.md.
Start here
- Reading a query and test — the first thing to read. Without this, every
.nlfile in the repo is unreadable because every example ends with tests in the? [facts][falsehoods]notation. - Your first program — line-by-line walkthrough of
fibonacci.nl, exercising imports, patterns, rules with guards, and tests.
Thirty minutes with those two pages is enough to open any .nl file in the repo and follow what's going on.
Reading paths
Different readers want different things. Pick the path that matches your goal.
"I want to understand what Nelumbo is and why."
- Reading a query and test — the language's headline feature, three-valued logic
- Architecture — how Nelumbo is layered — Java core →
lang.nl(syntactic bootstrap) →logic.nl(three-valued logic +<=>/fact/?) → numeric/data stdlib → user code - Three-valued logic — the semantic foundation, with truth tables
nelumbo.lang— the.nlfile that declares the whole syntax, including::,::=,::>, and the pattern meta-grammarnelumbo.logic— the.nlfile that declaresBoolean, the connectives, equality, and thefact/<=>/?statement forms
"I want to write my own DSL using Nelumbo."
- The starting pair above, then
- Grammar and Operators — reference
- Writing rules —
<=>, guards, merging, contradictions - Language transformations — the
::>mechanism for defining new keywords (status: under construction) - Writing your own module — packaging a reusable library
"I want to extend Nelumbo with a new primitive in Java."
- Architecture — understand the layering first
- Native API — the
Predicate,InferResult,infer()surface - Native classes catalogue — what's already shipped
- Native cookbook — five recipes with complete skeletons
"I want to read the stdlib to learn idiom."
- Standard library tour — all seven modules in dependency order
stdlib/lang.md,logic.md,integers.md,rationals.md,strings.md,collections.md,datetime.md— per-module reference
"I need to look something up."
Jump straight to the reference section below. Everything in reference/ is organised as a lookup, not a read-through.
Full table of contents
Getting started
Reference
The irreducible facts about the language.
- Grammar — the core syntax of
.nlfiles - Operators — complete operator catalogue
- Built-in tokens and pattern holes —
<NUMBER>,<STRING>,<Variable>, etc. - Precedence and associativity — the
#Nsystem - Visibility —
private,hidden,visible, and scope blocks - Three-valued logic — truth tables and identities
- Test expression semantics — formal definition of when a test passes
- Writing rules —
<=>, guards, merging, contradictions - Native API — implementing predicates in Java
- Native classes catalogue — every shipped
@-bound class - Formatting — the canonical whitespace layout the LSP formatter produces
Standard library reference
nelumbo.lang— the bootstrap layer: token types, object hierarchy, pattern meta-grammar, top-level statementsnelumbo.logic— Boolean, connectives, quantifiers, equalitynelumbo.integers— arbitrary-precision integer arithmeticnelumbo.rationals— exact rationalsnelumbo.strings— strings, concatenation, conversionnelumbo.collections— genericSet<E>andList<E>, set-builder notation, and operations (cardinality, membership, subset, union/intersection/difference, concatenation, indexing)nelumbo.datetime— ISO 8601 dates, times, date-times, and durations
Guides
Task-oriented how-tos.
- Standard library tour — reading all seven stdlib modules in order
- Writing your own module — packaging a reusable library
- Language transformations — the
::>meta-feature (under construction) - Native cookbook — recipes for writing Java natives
Explanation
Background and design.
Map of the example files
The .nl files under src/main/resources/org/modelingvalue/nelumbo/examples/ are the canonical worked examples. A short guide to what's in each:
| File | What it demonstrates |
|---|---|
fibonacci.nl | Integer arithmetic, recursive rule with guards, basic tests |
family.nl | Types, subtypes, FactType, literal enumerations, ancestor/descendant recursion |
friends.nl | Mutually-recursive relations, symmetric + transitive closure |
even.nl | Simplest useful predicate using an existential quantifier |
max.nl, ternary.nl | Generic types (Type T), conditional expressions, user-defined operators |
power.nl, maxFib.nl | Composed recursive definitions |
belasting.nl | Natural-language DSL (Dutch tax rules) using multi-word pattern syntax |
transformation.nl, deHet.nl | Language transformations with ::> — defining new top-level keywords |
scoping.nl, hidden.nl | Scope blocks and hidden-variable patterns |
whoIs.nl | Importing a user-written module (not just stdlib) |
queryOnly.nl | Bare queries without [..][..] — useful during development |
*Assignment.nl | "Fill in the rules" exercise versions of the corresponding examples |
Map of the test files
The *Test.nl files under src/main/resources/org/modelingvalue/nelumbo/tests/ are executable specifications — useful to skim for corner cases:
| File | What it specifies |
|---|---|
logicTest.nl | The three-valued logic truth tables |
integersTest.nl, rationalsTest.nl, stringsTest.nl, collectionsTest.nl | Per-stdlib-module behaviour for each data type |
datetimeTest.nl | Every datetime operator and the parse-time edge cases |
langTest.nl | Smoke test that imports nelumbo.lang on its own |
A note on conventions used in the docs
..is the incompleteness marker — it means "this side of the result may have more bindings than are listed." It is never called a wildcard because it doesn't match values.- Facts (left bracket) and falsehoods (right bracket) are the two sides of every query result.
- In-language extensions (rules, transformations, modules) are distinguished from host-language extensions (Java natives bound with
@). The docs use these terms consistently. - Cross-references in every page point to other pages when a topic is covered in more depth elsewhere. If you follow the links, you end up with a coherent tour of the language.
Feedback and contributing
The documentation is new and will have errors. Corrections and suggestions are welcome via the GitHub issue tracker. The source files for the docs are under docs/ in the repository.