Language Reference
- Use this page as a map after learning each topic on its dedicated guide page.
- Nenu has a small explicit grammar; syntax borrowed from JavaScript, Rust, or SQL is usually wrong.
- Fix the first precise diagnostic, then recheck later errors in context.
File And Declaration Map
| Intent | Canonical form | Context |
|---|---|---|
| Define durable data | entity Expense { ... } | Entity file |
| Define a named shape | type ExpenseSummary { ... } | Shared declaration |
| Define variants | enum ExpenseStatus { ... } | Shared declaration |
| Define defaults | default Expense { ... } | Full/backend declaration |
| Define a function | ReturnType fn name(Type input) { ... } | Supported profile rules apply |
| Attach an entity method | fn settle of Expense { ... } | Entity behavior |
| Write a runnable | Write executable statements directly | API, Cron, or Listener file |
| Write Page Script | Write state, functions, imports, and effects directly | Page or component script |
Statement Map
| Intent | Canonical syntax |
|---|---|
| Declare typed local | string title = "Groceries" |
| Declare inferred local | title = "Groceries" |
| Bind a reference path | alias note = expense.note |
| Assign | expense.status = ExpenseStatus.Settled |
| Branch | if condition { ... } else { ... } |
| Iterate array | loop expenses as expense { ... } |
| Iterate map | loop totals as key, value { ... } |
| Guard presence | with value as present { ... } |
| Guard absence | without value { ... } |
| Match variants | match status { to Status.Open { ... } } |
| Exit loop iteration | continue |
| Exit loop | break |
| Return value | return value |
| Fail backend path | throw Validation("message") |
| Backend log | log("event", optional_payload) |
| Cache operation | cache.set("key", value) |
Data And Runnable Map
| Intent | Canonical syntax |
|---|---|
| Fetch required by ID | need expense = Expense(id) |
| Fetch optional by ID | need expense = Expense?(id) |
| Fetch many as array | need expenses = Expense[](ids) |
| Fetch many as map | need expenses = Expense{}(ids) |
| Filtered query | need expenses = Expense[]({ room_id == id }) |
| Join | need rows = Expense[]({}) join Room(room_id) |
| Persist one or more | save(expense, settlement) |
| Persist transactionally | save.transaction(expense, settlement) |
| Delete fetched value | delete expense |
| Delete by expression | delete Expense(id) |
| Return API success | return Response { expense } |
Page Script Map
| Intent | Canonical syntax |
|---|---|
| Declare inputs | need { room_id: string } |
| Import API | use api/expenses/list as ListExpensesApi |
| Import component | use library/AddExpense as AddExpenseModal |
| Initial effect | on load() { ... } |
| Reactive effect | on change(query, expenses) { ... } |
| Navigate | goto("/expenses/[id]", { id }) |
| Open modal | modal.show(AddExpenseModal { ... }) |
| Close modal | modal.close() |
| Show feedback | toast.success("Saved") |
Common Mistakes
| Wrong | Correct |
|---|---|
do { ... } | Write statements directly. |
set expense.amount = 10 | expense.amount = 10 |
new Expense { ... } | Expense { ... } |
let title = "x" | string title = "x" or title = "x" |
needs { ... } | need { ... } |
save expense | save(expense) |
save [expense] | save(expense) |
Expense.find({ ... }) | Expense[]({ ... }) |
Expense[](ids) | map | Expense{}(ids) |
for expense in expenses | loop expenses as expense |
switch and case | match and to |
null or undefined | ? |
condition ? a : b | condition : a | b |
respond { ... } | return Response { ... } |
Direct Page Script api(...) | Import a route and call its alias. |
Words From Other Languages
Do not write set, new, var, const, let, do, while, foreach, switch, case, try, catch, finally, async, await, null, nil, undefined, print, class, struct, interface, endpoint, handler, runnable, or needs. Use the documented Nenu form for the intent instead.
Some lexer-reserved words are not complete public authoring forms by themselves. In particular, do not invent syntax around admin, returns, for, or find. clone is reserved for server-side entity duplication: Page Script rejects receiver calls such as entity.clone(), and standalone clone(entity) is not a public expression form. Use only a host-supported clone workflow rather than guessing a portable statement. Use the dedicated construction, access, return, loop, and query forms documented in this guide.
Profile Checklist
| Question | Direction |
|---|---|
| Does the code fetch entities, save, delete, cache, log, or throw? | Use a full/backend runnable. |
| Does it handle page state or a visual event? | Use Page Script. |
| Does Page Script need durable data? | Create or import an API. |
| Does an API need Request or Response fields? | Define them in API metadata. |
| Does a Listener need event inputs? | Wait for the listener payload-contract redesign; request fields are not part of current metadata. |
| Does a Cron need a schedule? | Define it in Cron metadata. |
| Does an empty collection need a type? | Write an explicit container declaration. |
| Does an optional need field access? | Use with, without, or ??. |
Reading Diagnostics
- Parse diagnostics mean the token sequence is not valid Nenu. Fix the earliest precise location first.
- Name diagnostics identify unknown bindings, fields, types, functions, routes, APIs, or component aliases.
- Type diagnostics mean a parsed value, assignment, call, collection, or return does not match its contract.
- Contract diagnostics compare API metadata with runnable source or an imported call, and validate required runnable metadata such as a Listener event key.
- Profile diagnostics mean a full/backend construct was used in Page Script or a page-only integration was used as backend syntax.
- Data diagnostics identify invalid need shapes, query operators, indexes, joins, dependencies, or cycles.
- Control-flow diagnostics identify missing returns, incompatible paths, invalid loop control, or unreachable statements.
One early parse or type error can make later diagnostics misleading. Correct the first concrete cause, re-run validation, and then evaluate what remains. Do not silence a diagnostic by changing the program’s contract unless that contract was actually wrong.
Debugging Workflow
- Confirm the file context: entity, API, Cron, Listener, page, or library component.
- Reduce the problem to the smallest valid declaration or runnable statements.
- Check the dedicated guide page for the construct instead of translating syntax from another language.
- Check API Request and Response contracts, Listener event keys, routes, or component inputs when the error crosses a metadata boundary.
- Validate again after the first fix before editing later locations.
Reference Boundaries
When the language specification, compiler, and App Marché integration differ in scope, the active file profile and compiler diagnostics decide what that file may use. This public guide labels those boundaries rather than pretending every parsed construct runs everywhere.
