Skip to content

Language Syntax

Complete reference for Zenoscript syntax and language features.

Variables and Constants

Let Bindings

typescript
// Immutable bindings (recommended)
let name = "Alice"
let age = 30
let isActive = true

// Type annotations (optional)
let score: number = 95.5
let message: string = "Hello"
let items: string[] = ["a", "b", "c"]

Variable Declarations

typescript
// Mutable variables (use sparingly)
var counter = 0
counter = counter + 1

Data Types

Basic Types

typescript
// Numbers
let integer = 42
let float = 3.14
let negative = -10

// Strings
let greeting = "Hello, World!"
let template = `Hello, ${name}!`
let multiline = `
  This is a
  multiline string
`

// Booleans
let isTrue = true
let isFalse = false

// Arrays
let numbers = [1, 2, 3, 4, 5]
let mixed = [1, "hello", true]
let empty: number[] = []

// Objects
let user = {
  name: "Alice",
  age: 30,
  active: true
}

Atoms

Type-safe constants using symbol syntax:

typescript
// Atom declaration
let :success = Symbol.for("success")
let :error = Symbol.for("error")
let :pending = Symbol.for("pending")

// Using atoms
let status = :pending
let result = :success

// Atoms in pattern matching
let message = match status {
  :pending => "Loading..."
  :success => "Done!"
  :error => "Failed!"
  _ => "Unknown"
}

Function Call Syntax

Optional Parentheses

Zenoscript supports optional parentheses for function calls, inspired by functional languages like Elixir:

typescript
// Traditional function calls
console.log("Hello")
processValue(42)
validateInput(userData)

// Optional parentheses syntax
console.log "Hello"
processValue 42
validateInput userData

// Works with different argument types
myFunction "string argument"
calculate 3.14
handleUser userObject
processArray [1, 2, 3]

// Multiple arguments still require parentheses
add(5, 10)  // Multiple args need parentheses

Smart keyword detection ensures control flow statements work correctly:

typescript
// Keywords are preserved
if condition { doSomething() }
let value = expression
return result

// Compiles correctly to:
if (condition) { doSomething(); }
const value = expression;
return result;

Function Return Statements

Functions automatically return their last expression, eliminating boilerplate:

typescript
// Concise single-expression functions
let add = (a, b) => { a + b }
let square = (x) => { x * x }
let greet = (name) => { `Hello, ${name}!` }

// Multi-statement functions with automatic return
let processUser = (user) => {
  let validated = validateUser(user)
  let normalized = normalizeData(validated)
  let enriched = enrichUserData(normalized)
  enriched  // Automatically returned
}

// Explicit returns still work
let explicitReturn = (x) => {
  if (x < 0) {
    return "negative"
  }
  return "positive"
}

// Mixed statements with automatic return
let complexFunction = (data) => {
  console.log("Processing data...")
  let result = processData(data)
  console.log("Processing complete")
  result  // This gets returned automatically
}

Compiles to TypeScript with explicit returns:

typescript
const add = (a, b) => { return a + b };
const square = (x) => { return x * x };

const processUser = (user) => {
  const validated = validateUser(user);
  const normalized = normalizeData(validated);
  const enriched = enrichUserData(normalized);
  return enriched;
};

Structs

Define structured data types:

typescript
// Simple struct
struct User {
  id: number;
  name: string;
  email: string;
}

// Generic struct
struct Container<T> {
  value: T;
  timestamp: number;
}

// Struct with optional fields
struct Config {
  host: string;
  port?: number;
  ssl?: boolean;
}

// Using structs
let user: User = {
  id: 1,
  name: "Alice",
  email: "alice@example.com"
}

let config: Config = {
  host: "localhost",
  port: 8080
}

Traits

Define interfaces and contracts:

typescript
// Simple trait
trait Drawable {
  draw(): void;
}

// Generic trait
trait Serializable<T> {
  serialize(): T;
  deserialize(data: T): this;
}

// Trait with default methods
trait Logger {
  log(message: string): void;
  
  // Default implementation
  logError(error: string): void {
    this.log(`ERROR: ${error}`)
  }
}

Functions

Function Declarations

typescript
// Arrow functions
let add = (a: number, b: number) => a + b
let greet = (name: string) => `Hello, ${name}!`

// Multi-line functions
let complexFunction = (x: number, y: number) => {
  let sum = x + y
  let product = x * y
  return { sum, product }
}

// Function with destructuring
let getName = ({ first, last }: { first: string, last: string }) => 
  `${first} ${last}`

// Higher-order functions
let map = <T, U>(array: T[], fn: (T) => U): U[] => 
  array.map(fn)

let filter = <T>(array: T[], predicate: (T) => boolean): T[] =>
  array.filter(predicate)

Currying

typescript
// Curried function
let multiply = (a: number) => (b: number) => a * b
let double = multiply(2)
let triple = multiply(3)

let result1 = double(5) // 10
let result2 = triple(4) // 12

// Partial application
let greetWith = (greeting: string) => (name: string) => 
  `${greeting}, ${name}!`

let sayHello = greetWith("Hello")
let sayGoodbye = greetWith("Goodbye")

Pattern Matching

Basic Matching

typescript
// Match expressions
let describe = (value) => match value {
  0 => "zero"
  1 => "one"
  2 => "two"
  _ => "many"
}

// Match with guards
let classify = (n: number) => match n {
  x if x < 0 => "negative"
  0 => "zero"
  x if x > 0 && x < 10 => "small positive"
  _ => "large positive"
}

Destructuring

typescript
// Array destructuring
let [first, second, ...rest] = [1, 2, 3, 4, 5]

// Object destructuring
let { name, age } = user
let { name: userName, age: userAge } = user

// Nested destructuring
let { user: { name }, config: { port } } = response

// Pattern matching with destructuring
let processResult = (result) => match result {
  { type: "success", data } => `Success: ${data}`
  { type: "error", message, code } => `Error ${code}: ${message}`
  { type: "pending" } => "Processing..."
  _ => "Unknown result"
}

List Patterns

typescript
// Match list patterns
let processItems = (items) => match items {
  [] => "empty list"
  [single] => `one item: ${single}`
  [first, second] => `two items: ${first}, ${second}`
  [head, ...tail] => `${head} and ${tail.length} more`
}

// Recursive list processing
let sum = (numbers) => match numbers {
  [] => 0
  [head, ...tail] => head + sum(tail)
}

Pipe Operations

Chain operations for readable data flow:

typescript
// Basic piping
let result = value
  |> transform
  |> validate
  |> process

// With function calls
let processed = data
  |> filter(x => x > 0)
  |> map(x => x * 2)
  |> reduce((a, b) => a + b, 0)

// String processing
let cleaned = input
  |> trim
  |> toLowerCase
  |> replace(/\s+/g, "-")

// Async piping
let response = await url
  |> fetch
  |> (res => res.json())
  |> validateResponse

Control Flow

Conditionals

typescript
// If expressions
let message = if (user.active) "Welcome!" else "Please activate"

// Multi-line if
let status = if (score >= 90) {
  "excellent"
} else if (score >= 70) {
  "good"
} else {
  "needs improvement"
}

// Ternary operator
let label = isActive ? "Active" : "Inactive"

Loops

typescript
// For loops (avoid in functional style)
for (let i = 0; i < items.length; i++) {
  console.log(items[i])
}

// For-of loops
for (let item of items) {
  console.log(item)
}

// Functional alternatives (preferred)
items |> forEach(item => console.log(item))
items |> map(item => item.toUpperCase())
items |> filter(item => item.length > 3)

Error Handling

Option Type Pattern

typescript
// Option type
let findUser = (id: number) => 
  users.find(u => u.id === id) ? 
    { type: :some, value: users.find(u => u.id === id) } :
    { type: :none }

// Using option
let userResult = match findUser(123) {
  { type: :some, value } => `Found: ${value.name}`
  { type: :none } => "User not found"
}

Result Type Pattern

typescript
// Result type for operations that can fail
let parseJson = (text: string) => {
  try {
    let data = JSON.parse(text)
    return { type: :ok, value: data }
  } catch (error) {
    return { type: :error, message: error.message }
  }
}

// Using result
let handleJson = (text) => match parseJson(text) {
  { type: :ok, value } => processData(value)
  { type: :error, message } => logError(message)
}

Modules and Imports

Importing

typescript
// ES module imports
import { readFile } from "fs/promises"
import express from "express"
import * as utils from "./utils"

// Zenoscript module imports
import { User, createUser } from "./models/user.zs"
import { DatabaseConfig } from "./config.zs"

Exporting

typescript
// Named exports
export let PI = 3.14159
export let square = (x: number) => x * x

export struct Point {
  x: number;
  y: number;
}

// Default export
let main = () => {
  console.log("Hello from Zenoscript!")
}

export default main

Comments

typescript
// Single line comment

/*
  Multi-line comment
  spanning multiple lines
*/

/**
 * Documentation comment
 * @param name The user's name
 * @returns A greeting message
 */
let greet = (name: string) => `Hello, ${name}!`

Type Annotations

typescript
// Variable annotations
let name: string = "Alice"
let age: number = 30
let active: boolean = true

// Function annotations
let add = (a: number, b: number): number => a + b
let greet = (name: string): string => `Hello, ${name}!`

// Generic annotations
let identity = <T>(value: T): T => value
let map = <T, U>(array: T[], fn: (T) => U): U[] => array.map(fn)

// Complex types
let user: { name: string; age: number } = { name: "Alice", age: 30 }
let callback: (error: Error | null, data?: any) => void = () => {}

Operators

Arithmetic

typescript
let a = 10 + 5   // Addition
let b = 10 - 5   // Subtraction  
let c = 10 * 5   // Multiplication
let d = 10 / 5   // Division
let e = 10 % 3   // Modulo
let f = 2 ** 3   // Exponentiation

Comparison

typescript
let eq = a === b  // Strict equality
let neq = a !== b // Strict inequality
let lt = a < b    // Less than
let lte = a <= b  // Less than or equal
let gt = a > b    // Greater than
let gte = a >= b  // Greater than or equal

Logical

typescript
let and = a && b  // Logical AND
let or = a || b   // Logical OR
let not = !a      // Logical NOT
let nullish = a ?? b  // Nullish coalescing

Assignment

typescript
let x = 5       // Assignment
x += 3          // Add and assign
x -= 2          // Subtract and assign
x *= 4          // Multiply and assign
x /= 2          // Divide and assign

This covers the essential syntax of Zenoscript. For more advanced patterns, check out the Advanced Examples.

Released under the MIT License.