nenu-lang / reference
Stable

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

IntentCanonical formContext
Define durable dataentity Expense { ... }Entity file
Define a named shapetype ExpenseSummary { ... }Shared declaration
Define variantsenum ExpenseStatus { ... }Shared declaration
Define defaultsdefault Expense { ... }Full/backend declaration
Define a functionReturnType fn name(Type input) { ... }Supported profile rules apply
Attach an entity methodfn settle of Expense { ... }Entity behavior
Write a runnableWrite executable statements directlyAPI, Cron, or Listener file
Write Page ScriptWrite state, functions, imports, and effects directlyPage or component script

Statement Map

IntentCanonical syntax
Declare typed localstring title = "Groceries"
Declare inferred localtitle = "Groceries"
Bind a reference pathalias note = expense.note
Assignexpense.status = ExpenseStatus.Settled
Branchif condition { ... } else { ... }
Iterate arrayloop expenses as expense { ... }
Iterate maploop totals as key, value { ... }
Guard presencewith value as present { ... }
Guard absencewithout value { ... }
Match variantsmatch status { to Status.Open { ... } }
Exit loop iterationcontinue
Exit loopbreak
Return valuereturn value
Fail backend paththrow Validation("message")
Backend loglog("event", optional_payload)
Cache operationcache.set("key", value)

Data And Runnable Map

IntentCanonical syntax
Fetch required by IDneed expense = Expense(id)
Fetch optional by IDneed expense = Expense?(id)
Fetch many as arrayneed expenses = Expense[](ids)
Fetch many as mapneed expenses = Expense{}(ids)
Filtered queryneed expenses = Expense[]({ room_id == id })
Joinneed rows = Expense[]({}) join Room(room_id)
Persist one or moresave(expense, settlement)
Persist transactionallysave.transaction(expense, settlement)
Delete fetched valuedelete expense
Delete by expressiondelete Expense(id)
Return API successreturn Response { expense }

Page Script Map

IntentCanonical syntax
Declare inputsneed { room_id: string }
Import APIuse api/expenses/list as ListExpensesApi
Import componentuse library/AddExpense as AddExpenseModal
Initial effecton load() { ... }
Reactive effecton change(query, expenses) { ... }
Navigategoto("/expenses/[id]", { id })
Open modalmodal.show(AddExpenseModal { ... })
Close modalmodal.close()
Show feedbacktoast.success("Saved")

Common Mistakes

WrongCorrect
do { ... }Write statements directly.
set expense.amount = 10expense.amount = 10
new Expense { ... }Expense { ... }
let title = "x"string title = "x" or title = "x"
needs { ... }need { ... }
save expensesave(expense)
save [expense]save(expense)
Expense.find({ ... })Expense[]({ ... })
Expense[](ids) | mapExpense{}(ids)
for expense in expensesloop expenses as expense
switch and casematch and to
null or undefined?
condition ? a : bcondition : 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

QuestionDirection
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.

Continue Learning