Syntax Reference

NAIDE uses indentation-based blocks, keyword-driven statements, and explicit type annotations. The first token on each line determines intent.

Variables

Variables are immutable by default (const). Use mut for mutable bindings (let).

str name = "hello"          # immutable (const)
int count = 42
num price = 9.99
bool active = true
list items = [1, 2, 3]
map config = {host: "localhost"}
any data = null

mut int counter = 0          # mutable (let)
mut str label = "init"

Available Types

TypeDescriptionJavaScript
strStringstring
intInteger numbernumber
numAny number (accepts int)number
boolBooleanboolean
listArrayArray
mapObject / dictionaryObject
anyAny type(untyped)
jsonJSON-compatible value(untyped)
voidNo return valuevoid

String Interpolation

Double-quoted strings with {expr} are automatically interpolated into template literals:

str greeting = "Hello {name}, you have {count} items"

Compiles to:

const greeting = `Hello ${name}, you have ${count} items`;

Functions

Functions use fn, with optional typed parameters and return type:

fn add(int a, int b) -> int:
  ret a + b

fn greet(str name, str prefix = "Hello"):
  log "{prefix}, {name}!"

Rest Parameters

fn sum(...int nums) -> int:
  mut int total = 0
  each n in nums:
    total += n
  ret total

Async Functions

fn.async fetchUser(str id) -> map:
  any res = await fetch("/api/users/{id}")
  ret await res.json()

Control Flow

If / Elif / Else

if count > 10:
  log "many"
elif count > 5:
  log "some"
else:
  log "few"

Ternary Expression

str size = if count > 10 then "big" else "small"

Loops

each — Iterate Arrays

each item in items:
  log item

each — Iterate Object Keys/Values

each key, val in config:
  log "{key}: {val}"

for — Numeric Range

for i in 0..10:
  log i

while

while active:
  log "running"
  active = false

Pattern Matching

match status:
  "ok": log "success"
  "error": log "failed"
  _: log "unknown"

Compiles to a switch statement. Use _ for the default case.

Error Handling

try:
  data = await fetchData(url)
fail e:
  log.error e.message
ensure:
  log "request completed"

fail is NAIDE's catch. ensure is NAIDE's finally — the block always runs whether the try succeeds or fails. Both fail and ensure are optional:

try:
  conn = await openConnection()
  ret await conn.query("SELECT 1")
ensure:
  conn.close()

Type Checking

if typeof data == "string":
  log "is string"

if err instanceof TypeError:
  log "type error"

typeof returns the type as a string. instanceof checks if a value is an instance of a class or constructor.

Classes (model)

Use model to define classes with typed properties:

model User:
  str name
  str email
  int age = 0

  fn greet() -> str:
    ret "Hi, I'm {self.name}"

model Admin extends User:
  str role = "admin"
  fn permissions() -> list:
    ret ["read", "write", "delete"]

self refers to the current instance (compiles to this).

Pipe Operator

list result = data
  |> filter((x) => x.active)
  |> map((x) => x.name)
  |> sort()

Pipes chain method calls for readable data transformations.

Imports & Exports

Imports

use express
use {readFile, writeFile} from "fs/promises"
use axios from "axios"

use compiles to import. When importing between NAIDE files, extensions are automatically rewritten to .mjs:

use {handleUser} from "./routes.naide"

Exports

pub fn helper() -> str:
  ret "exported"

pub str VERSION = "1.0.0"

pub marks functions and variables as exported (export).

Environment Variables

env:
  PORT       int default(3000)
  JWT_SECRET str required
  DB_URL     str default("data/")

Variables become constants read from process.env. Missing required variables cause the process to exit with an error.

Built-in Functions

str id = uuid()                          # UUID generation
str hashed = hash("password")            # scrypt hash
bool ok = verify("password", hashed)     # timing-safe verify
str token = sign({id: 1})                # JWT (uses auth secret)
str token = sign({id: 1}, "my-secret")   # JWT (explicit secret)

These are auto-imported from the NAIDE runtime when used.

HTTP Client

fn.async getUsers() -> any:
  any users = await api.get("https://api.example.com/users")
  ret users

fn.async createUser(map data) -> any:
  any result = await api.post("https://api.example.com/users", data)
  ret result

Available methods: api.get(url), api.post(url, body), api.put(url, body), api.del(url), api.raw(url, opts).

Plugin System

registerPlugin("logger", (opts) =>
  ret {log: (msg) => log "[{opts.prefix}] {msg}"}
)

any logger = usePlugin("logger", {prefix: "APP"})
logger.log("started")

list names = listPlugins()

registerPlugin(name, setup) registers a plugin factory. usePlugin(name, opts?) initializes on first call and returns cached exports. listPlugins() returns registered plugin names.