The LIPI Manual.
Complete reference for the LIPI programming language — from syntax fundamentals to real-world algorithm design. Everything documented here reflects the actual behavior of the v3.0 engine.
What is LIPI?
LIPI is a dynamically-typed, interpreted programming language that compiles to JavaScript at runtime inside the browser. Its pipeline is: Lexer → Parser → AST → Code Generator → JS Execution. It is designed to feel like a clean hybrid of Python and JavaScript — with Python's readable syntax and JavaScript's direct access to the browser's DOM.
LIPI is not a toy or a demo language. It features a full operator precedence parser (Pratt parser), a proper abstract syntax tree, a scope-aware code generator, and a production-grade runtime with 30+ built-in functions. Every feature documented here works exactly as described.
No semicolons. No curly-brace ceremony for conditions. Indentation is used for readability. Blocks are delimited with {}.
HTML element selection and event binding are first-class language features. No framework or library needed to manipulate the page.
Async is invisible. Call wait(ms) and execution pauses cleanly. No async/await keywords required in your code.
LIPI's primary use case is writing logic for interactive browser-based applications, visual simulations, algorithm demonstrations, and educational tooling. If you can think it in pseudocode, you can write it in LIPI.
Getting Started
LIPI requires no installation, no build step, and no package manager. Include the engine script in your HTML page. The engine auto-initializes on DOMContentLoaded.
<!-- Step 1: Include the engine --> <script src="lipi.js"></script> <!-- Step 2: Write LIPI code with Lipi.run() --> <script> document.addEventListener('DOMContentLoaded', () => { Lipi.run(` log("Hello from LIPI!") let x = 10 + 5 log("Result: " + x) `); }); </script>
In the LIPI Studio editor, you write code directly — no setup at all. The main.lipi tab is your logic file; the index.html tab is your layout. Click RUN or press Ctrl+Enter to execute.
# Your first LIPI program let language = "LIPI" let version = 11.0 let ready = true log("Language: " + language) log("Version: " + version) log("Ready: " + ready)
How LIPI Runs Code
Understanding how LIPI executes code will help you write better programs and debug unexpected behavior. The pipeline has four stages:
Your source text is scanned character-by-character and converted into a flat stream of typed tokens: keywords, identifiers, strings, numbers, operators, and punctuation. Unknown characters cause a LexError immediately.
The token stream is parsed using a Recursive Descent parser with a Pratt (top-down operator precedence) expression parser. This produces a typed Abstract Syntax Tree. Invalid syntax causes a ParseError with line numbers.
The AST is walked and each node is emitted as equivalent JavaScript. The generator wraps output in an async IIFE so that wait() can pause execution transparently using await. User-declared functions are tracked and awaited at all call sites.
The generated JavaScript is executed via new Function() in the current page scope. All browser globals (Math, JSON, Object, Array, etc.) are accessible. Runtime errors are caught and displayed with full context.
await in your code?LIPI's code generator automatically inserts await in the compiled JavaScript for any call to wait(), getInput(), or any function you define with func. This means your LIPI code reads like synchronous code while executing asynchronously under the hood.
Variables & Types
All variables are declared with the let keyword. LIPI is dynamically typed — a variable can hold any value and its type can change during execution. There is no const or var.
# All five primitive types let name = "Ram" # String let score = 98.5 # Number (integer or float) let active = true # Boolean let nothing = null # Null let pending = undefined # Undefined log(type(name)) # "string" log(type(score)) # "number" log(type(active)) # "boolean" log(type(nothing))# "null"
Comments
Two comment styles are supported. Both are stripped before compilation.
# Hash comment — Python-style, can appear on its own line let x = 10 # inline comment after a value token // Double-slash comment — JavaScript-style let y = 20 // also works inline
String Literals
Strings can use single quotes, double quotes, or backticks. Backtick strings support interpolation with {expression}.
let name = "World" let count = 3 # Standard concatenation log("Hello, " + name + "!") # Backtick interpolation (cleaner for complex strings) log(`Hello, {name}! You have {count} messages.`) # Escape sequences work in all string types log("Line one\nLine two") log("Tab:\there")
LIPI uses JavaScript's function-level scoping. Variables declared with let inside a block (if, while, for, func) are scoped to that block. Variables declared at the top level are available throughout the script execution.
Operators & Expressions
LIPI supports a comprehensive set of operators. Expressions follow standard mathematical precedence rules, and all operators are parsed by the Pratt parser for correct associativity.
let a = 10 let b = 3 log(a + b) # 13 — addition log(a - b) # 7 — subtraction log(a * b) # 30 — multiplication log(a / b) # 3.33 — division log(a % b) # 1 — modulo log(a // b) # 3 — floor division log(a ** b) # 1000 — exponentiation
let x = 5 log(x == 5) # true — strict equal log(x != 3) # true — not equal log(x > 3) # true — greater log(x <= 5) # true — less or equal log(x > 3 and x < 10) # true log(x == 1 or x == 5) # true log(not false) # true
Assignment Operators
let n = 10 n += 5 # n = 15 (add and assign) n -= 3 # n = 12 (subtract and assign) n *= 2 # n = 24 (multiply and assign) n //= 5 # n = 4 (floor-divide and assign) n **= 3 # n = 64 (exponentiate and assign) n %= 10 # n = 4 (modulo and assign) log(n)
Increment / Decrement
let i = 0 i++ # postfix increment: i = 1 i++ # i = 2 --i # prefix decrement: i = 1 log(i) # 1
Ternary Expression
LIPI uses Python-style ternary syntax: value_if_true if condition else value_if_false.
let age = 20 let status = "adult" if age >= 18 else "minor" log(status) # "adult" let score = 72 let grade = "A" if score >= 90 else "B" if score >= 70 else "C" log(grade) # "B"
Operator Precedence
| Precedence | Operator(s) | Description |
|---|---|---|
| 7 (highest) | ** | Exponentiation (right-associative) |
| 6 | * / % // | Multiply, divide, modulo, floor division |
| 5 | + - | Addition, subtraction |
| 4 | < <= > >= | Comparison |
| 3 | == != | Equality (strict) |
| 2 | and | Logical AND |
| 1 (lowest) | or | Logical OR |
Control Flow
LIPI supports if, else if, and else. Curly braces {} are always required around blocks. The condition does not need parentheses.
let temp = 22 if temp > 35 { log("Extreme heat warning") } else if temp > 25 { log("Warm day") } else if temp > 15 { log("Comfortable temperature") } else { log("Cold conditions") }
Compound Conditions
let user = "Ram" let role = "admin" let active = true if active and role == "admin" { log("Access granted to " + user) } if role == "guest" or not active { log("Limited access") } else { log("Full access") }
Assert
Use assert for defensive programming. If the condition is false, an error is thrown immediately with an optional message.
let balance = 100 # Will pass — balance is positive assert balance > 0, "Balance must be positive" let items = [1, 2, 3] assert len(items) == 3 log("All assertions passed")
Use typeof x to inspect the runtime type of a value. Returns "string", "number", "boolean", "object", "undefined", or "function". For arrays and null, use the type() builtin instead, which returns "array" and "null" correctly.
Loops
LIPI has two loop constructs: while for condition-based iteration, and for...in for iterating over collections. Both support break and continue.
While Loop
let fuel = 5 while fuel > 0 { log("Fuel remaining: " + fuel) fuel-- wait(200) } log("Tank empty.")
For-In: Arrays
When the iterable is an array or string, for x in collection iterates over the values.
let planets = ["Mercury", "Venus", "Earth", "Mars"] for planet in planets { log("Planet: " + planet) }
For-In: Objects
When the iterable is an object, for k in obj iterates over the keys (property names).
let config = {host: "localhost", port: 8080, debug: true} for key in config { log(key + " = " + config[key]) }
For-In: Numbers (Range Shorthand)
When the iterable is a number n, it iterates from 0 to n-1. For custom ranges, use the range() builtin.
# Iterate 0..4 for i in 5 { log("i = " + i) } # range(start, end) — does NOT include end for n in range(1, 6) { log(n) } # range(start, end, step) for x in range(0, 10, 2) { log(x) # 0, 2, 4, 6, 8 }
Break & Continue
# break — exit the loop early for i in 10 { if i == 4 { break } log("i = " + i) # logs 0, 1, 2, 3 } # continue — skip the rest of this iteration for n in range(1, 8) { if n % 2 == 0 { continue } log(n) # logs 1, 3, 5, 7 }
Functions
Functions are declared with func. They can accept parameters and return a value with return. All user-declared functions are internally async, which means they can call wait() and other async functions freely.
# Basic function with parameters and return func add(a, b) { return a + b } # Functions can return any type func greet(name) { return "Hello, " + name + "!" } # Functions without return implicitly return undefined func printLine(msg) { log("[LOG] " + msg) } let sum = add(12, 8) log(sum) # 20 log(greet("Ram")) # "Hello, Ram!" printLine("test")
Recursive Functions
func factorial(n) { if n <= 1 { return 1 } return n * factorial(n - 1) } for i in range(1, 8) { log(`{i}! = {factorial(i)}`) }
Functions with Async Logic
Because all func declarations are async, they can freely call wait() and other user-defined functions without any special syntax.
func countdown(from) { let n = from while n > 0 { log("T-minus: " + n) wait(400) n-- } log("🚀 Liftoff!") } countdown(5)
LIPI functions are declared sequentially. You must declare a function before calling it in top-level code. Mutual recursion (two functions calling each other) works because the compiler registers all declared function names before generating call sites.
Arrays
Arrays are ordered, zero-indexed, mutable collections. They can hold any mix of types. Use [] to create an array and arr[i] for indexed access.
let scores = [95, 87, 62, 78, 91] # Index access (zero-based) log(scores[0]) # 95 log(scores[len(scores) - 1]) # 91 (last element) # Mutation scores[2] = 70 log(scores) # Array size log("Count: " + len(scores)) # Append / pop append(scores, 100) log(scores) pop(scores) log(scores)
Functional Operations
let nums = [3, 1, 4, 1, 5, 9, 2, 6] log(sum(nums)) # 31 log(avg(nums)) # 3.875 log(max(nums)) # 9 log(min(nums)) # 1 log(sort(nums)) # [1, 1, 2, 3, 4, 5, 6, 9] log(reverse(nums)) # reversed copy log(includes(nums, 9)) # true log(indexOf(nums, 4)) # 2 log(slice(nums, 2, 5)) # [4, 1, 5] log(join(nums, ", ")) # "3, 1, 4, ..."
Arrays are reference types. Assigning let b = a makes both variables point to the same array. To create an independent copy, use copy(arr) for a shallow copy or deepCopy(arr) for a deep copy of nested structures.
Objects
Objects are unordered key-value maps. Keys are strings. Values can be any type, including nested objects or arrays. Use dot notation for static keys and bracket notation for dynamic or computed keys.
let user = { name: "ram", age: 30, roles: ["editor", "viewer"], address: { city: "Mumbai", zip: "400001" } } # Dot notation log(user.name) log(user.address.city) # Bracket notation (dynamic key) let field = "age" log(user[field]) # Mutation user.age = 31 user["email"] = "ram@example.com" # Delete a property delete user.address # Enumerate keys for key in user { log(`{key}: {user[key]}`) }
Object Inspection
let config = {host: "localhost", port: 3000, ssl: false} log(keys(config)) # ["host", "port", "ssl"] log(values(config)) # ["localhost", 3000, false] log(len(config)) # 3 log(has(config, "port")) # true log(has(config, "auth")) # false # Merge two objects (non-destructive) let override = {port: 443, ssl: true} let final = merge(config, override) log(final)
JSON Serialization
let data = {name: "Ram", scores: [90, 85, 92]} # Serialize to JSON string let json = toJSON(data) log(json) # Parse back from string let restored = fromJSON(json) log(restored.name)
DOM Selection
LIPI provides two syntaxes for selecting DOM elements. Both search within the preview container first, then fall back to the full document.
#elementId
Prefix-hash notation. Selects by element ID. The # is a language-level token that compiles to a DOM lookup.
let btn = #submit-btn
$("selector")
Accepts any CSS selector string — IDs, classes, attributes, or compound selectors.
let btn = $("#submit-btn")
# Shorthand selector — compiles to document.getElementById("title") let title = #page-title # Always check if the element was found before using it if title == null { log("Element not found!") } else { log("Found: " + title.id) } # Select multiple elements by CSS class let cards = queryAll(".card") log("Found " + len(cards) + " cards")
In LIPI Studio, selectors search the Live Preview pane first. Elements in your index.html tab are the ones accessible at runtime. Elements in the editor UI itself are not targeted.
Styles & Content
Once you have a reference to a DOM element, you can read and write its content and styles directly. LIPI also provides convenience style shortcut aliases so you can write box.style.bg instead of box.style.backgroundColor.
let card = #my-card # Content card.innerText = "Updated text" card.innerHTML = "<strong>Bold</strong> content" # Style — standard CSS property names card.style.backgroundColor = "#7c3aed" card.style.fontSize = "18px" card.style.display = "none" # Style — LIPI shorthand aliases (same result) card.style.bg = "#7c3aed" # backgroundColor card.style.fg = "white" # color card.style.size = "18px" # fontSize card.style.radius = "12px" # borderRadius card.style.shadow = "0 4px 12px rgba(0,0,0,.4)"
Full Style Shortcut Reference
| LIPI Shortcut | CSS Property | Example Value |
|---|---|---|
| style.bg | backgroundColor | "#7c3aed" or "red" |
| style.fg | color | "white" or "#fff" |
| style.size | fontSize | "16px" or "1.2em" |
| style.weight | fontWeight | "bold" or "600" |
| style.radius | borderRadius | "8px" or "50%" |
| style.shadow | boxShadow | "0 4px 12px rgba(0,0,0,.3)" |
| style.opacity | opacity | "0.5" or "1" |
| style.cursor | cursor | "pointer" or "default" |
| style.transition | transition | "all 0.3s ease" |
| style.display | display | "flex", "none", "block" |
DOM Helper Functions
let el = #status # Text and HTML content helpers setText(el, "Hello World") # sets innerText safely setHTML(el, "<em>italic</em>") # sets innerHTML # Visibility show(el) # removes display:none hide(el) # sets display:none # CSS class management addClass(el, "active") removeClass(el, "inactive") toggleClass(el, "highlighted") # Clear content clear(el)
Events
The on keyword binds an event listener to a DOM element. The syntax is on target.eventName { ... }. The target must be a variable holding a DOM element, or the #id shorthand used directly.
let btn = #action-btn let input = #text-input # Click event on btn.click { log("Button was clicked") } # Mouse enter/leave on btn.mouseenter { btn.style.bg = "#5b21b6" } on btn.mouseleave { btn.style.bg = "#7c3aed" } # Input change on input.input { log("Value: " + input.value) }
Async Inside Events
Event handlers are automatically async, so wait() and user functions work seamlessly inside them.
let box = #demo-box let count = 0 on box.click { count++ box.innerText = "Processing..." box.style.bg = "#f59e0b" wait(500) box.innerText = "Done! (click " + count + ")" box.style.bg = "#10b981" wait(800) box.style.bg = "#7c3aed" box.innerText = "Click Me" }
Common Event Names
| Event | Trigger |
|---|---|
| click | Mouse click or touch tap |
| mouseenter / mouseleave | Mouse enters or leaves element bounds |
| mouseover / mouseout | Mouse over (includes children) |
| input | Input field value changes |
| change | Input loses focus after changing |
| keydown / keyup | Keyboard key pressed/released |
| submit | Form is submitted |
| focus / blur | Element receives/loses focus |
| scroll | Element is scrolled |
Async & wait()
wait(ms) pauses code execution for the given number of milliseconds. Unlike JavaScript's setTimeout, it feels synchronous — the next line of code runs only after the wait completes. This is enabled by the async IIFE wrapper the code generator produces.
log("Starting process...") wait(600) log("Step 1 complete") wait(600) log("Step 2 complete") wait(600) log("All done! ✅")
Animated DOM Updates
Combine wait() with DOM updates to build smooth animations and step-by-step visualizations directly in Lipi code.
# Cycle through a color palette with delays let colors = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#8b5cf6"] let box = #demo-box for c in colors { box.style.bg = c box.innerText = c wait(400) } box.innerText = "Done"
When LIPI compiles wait(500), it emits await __wait(500) in JavaScript. The surrounding async IIFE pauses at this line for 500ms before continuing. The browser UI remains fully responsive during the pause — only your code is waiting, not the page.
User Input
getInput("prompt") pauses execution and waits for the user to type something in the console. In the Studio, a live input field appears in the console panel. The function returns the entered string.
let name = getInput("What is your name?") log("Hello, " + name + "!") let numStr = getInput("Enter a number:") let num = int(numStr) log("Squared: " + (num ** 2))
getInput() always returns a string value. If you need to do arithmetic with the result, convert it first with int() or float(). Empty input returns an empty string "".
Math & Numbers
All standard math operations are available as top-level functions. The global Math object is also accessible directly.
log(abs(-42)) # 42 log(floor(3.9)) # 3 log(ceil(3.1)) # 4 log(round(3.567, 2)) # 3.57 log(sqrt(144)) # 12 log(pow(2, 10)) # 1024 log(max(10, 3, 7)) # 10 log(min(10, 3, 7)) # 3 log(random(1, 100)) # random integer in [1, 99] log(random()) # float in [0, 1) # Constants log(PI) # 3.141592653589793 log(E) # 2.718281828459045
String Helpers
let msg = " Hello, World! " log(trim(msg)) # "Hello, World!" log(upper(msg)) # " HELLO, WORLD! " log(lower(msg)) # " hello, world! " log(replace(msg, "World", "LIPI")) # " Hello, LIPI! " log(len(msg)) # 18 let csv = "one,two,three" let parts = split(csv, ",") log(parts) # ["one", "two", "three"] log(join(parts, " | ")) # "one | two | three" log(startsWith(csv, "one")) # true log(contains(csv, "two")) # true log(repeat("ab", 3)) # "ababab"
Array Helpers
| Function | Returns | Description |
|---|---|---|
| len(arr) | number | Number of elements |
| append(arr, val) | array | Add to end (mutates) |
| prepend(arr, val) | array | Add to start (mutates) |
| pop(arr) | value | Remove and return last element |
| remove(arr, idx) | — | Remove element at index |
| slice(arr, a, b) | array | Extract sub-array [a, b) |
| reverse(arr) | array | Returns reversed copy |
| sort(arr) | array | Returns sorted copy |
| includes(arr, val) | boolean | True if val is in array |
| indexOf(arr, val) | number | First index of val, or -1 |
| find(arr, fn) | value | First element matching predicate |
| filter(arr, fn) | array | All elements matching predicate |
| map(arr, fn) | array | Transform each element |
| flat(arr, depth) | array | Flatten nested arrays |
| sum(arr) | number | Sum of all numeric elements |
| avg(arr) | number | Average of all numeric elements |
| copy(arr) | array | Shallow copy |
| deepCopy(arr) | array | Deep copy (JSON-safe) |
let data = [4, 8, 15, 16, 23, 42] # filter: keep only even numbers (using JS arrow syntax) let evens = filter(data, n => n % 2 == 0) log(evens) # [4, 8, 16, 42] # map: double each value let doubled = map(data, n => n * 2) log(doubled) # find: first value over 20 let big = find(data, n => n > 20) log(big) # 23
Functions like filter(), map(), and find() accept JavaScript arrow functions (n => n * 2) as callbacks. These are passed through verbatim to the compiled output, so standard JavaScript callback syntax is fully supported as function arguments.
DOM Helper Functions
| Function | Description |
|---|---|
| $(selector) | Select element by CSS selector |
| queryAll(selector) | Select all matching elements (returns array) |
| setText(el, text) | Set innerText safely |
| setHTML(el, html) | Set innerHTML |
| getText(el) | Get innerText |
| getHTML(el) | Get innerHTML |
| show(el) | Remove display:none |
| hide(el) | Set display:none |
| clear(el) | Empty innerHTML |
| addClass(el, cls) | Add CSS class(es) |
| removeClass(el, cls) | Remove CSS class(es) |
| toggleClass(el, cls) | Toggle a CSS class |
| hasClass(el, cls) | Check if class is present |
| appendTo(parent, child) | Append child element to parent |
Type Conversion
# int() — parse string to integer let n = int("42") log(n + 8) # 50 # float() — parse string to float let pi = float("3.14159") log(round(pi, 2)) # 3.14 # str() — convert any value to string let s = str(99) log(type(s)) # "string" # bool() — convert to boolean log(bool(0)) # false log(bool("")) # false log(bool("hello")) # true # type() — returns type as string log(type([1,2,3])) # "array" log(type(null)) # "null" log(type(true)) # "boolean"
Error Handling
LIPI errors fall into three categories. All errors display in the Studio's console and as an overlay notification.
Caused by an unrecognized character in your source code. Includes line number.
Caused by invalid syntax — a missing brace, wrong keyword order, unexpected token. Includes line number and what was expected.
Caused by logic errors at execution time — calling a method on null, type mismatches, assertion failures, etc.
Debugging Tips
Print variables at key points to trace execution flow and validate intermediate values.
Check if element == null before accessing properties. Selector failures are the most common runtime error.
Use assert condition, "message" to enforce assumptions about your data at critical points in algorithms.
If a variable might come from getInput() or an array, convert it with int() or float() before doing math.
Best Practices
These patterns reflect how LIPI is designed to be used. They will save you from the most common classes of bugs.
# Guard before accessing let el = #my-box if el != null { el.innerText = "Safe" }
Always check DOM selectors before use.
# This crashes if #my-box is missing let el = #my-box el.innerText = "Unsafe"
Don't access properties without a null check.
# Convert input before math let raw = getInput("Number:") let n = int(raw) log(n * 2)
Always convert input to the correct type.
# "5" * 2 works, but "5" + 2 = "52" let raw = getInput("Number:") log(raw + 10) # Concatenates!
Don't add numbers to raw string input.
Structure complex programs by declaring helper functions at the top, then calling them at the bottom. Keep each function focused on a single responsibility. Use objects to group related state, and arrays as ordered queues or stacks.
For data that changes over time (e.g., simulation state), store it in a top-level let variable. Pass it to functions by reference — objects and arrays are reference types, so mutations inside a function affect the original.
Real-World Examples
These examples demonstrate LIPI's capability for real algorithmic work. Each one runs in the console. Click RUN to execute.
Fibonacci with Memoization
Classic dynamic programming example. Stores computed results in an object to avoid redundant recursive calls.
let cache = {} func fib(n) { if n <= 1 { return n } if has(cache, str(n)) { return cache[str(n)] } let result = fib(n - 1) + fib(n - 2) cache[str(n)] = result return result } for i in range(1, 16) { log(`fib({i}) = {fib(i)}`) }
State Machine
Models a system that transitions between named states based on events — a common pattern for UI flows, game logic, and network protocols.
let states = { idle: {on_start: "running"}, running: {on_pause: "paused", on_stop: "idle", on_error: "error"}, paused: {on_resume: "running", on_stop: "idle"}, error: {on_reset: "idle"} } let current = "idle" func dispatch(event) { let map = states[current] let key = "on_" + event if has(map, key) { let next = map[key] log(`[{current}] --{event}--> [{next}]`) current = next } else { log(`Invalid: '{event}' in state '{current}'`) } } dispatch("start") dispatch("pause") dispatch("stop") # Invalid from paused dispatch("resume") dispatch("error") dispatch("reset") log("Final state: " + current)
Bubble Sort with Visualization
Implements bubble sort and logs each pass, demonstrating loops, mutation, and algorithmic thinking.
func bubbleSort(arr) { let n = len(arr) let sorted = copy(arr) let passes = 0 for i in range(n) { let swapped = false for j in range(n - i - 1) { if sorted[j] > sorted[j + 1] { let tmp = sorted[j] sorted[j] = sorted[j + 1] sorted[j + 1] = tmp swapped = true } } passes++ if not swapped { break } } log(`Completed in {passes} passes`) return sorted } let data = [64, 34, 25, 12, 22, 11, 90] log("Input: " + join(data, ", ")) let result = bubbleSort(data) log("Output: " + join(result, ", "))
Memory Block Allocator
Simulates a simple block-based memory allocator. Demonstrates objects as mutable state containers, dynamic property keys, and has() for existence checking.
let heap = {capacity: 32, used: 0, blocks: {}} let nextId = 0 func alloc(size) { if heap.used + size > heap.capacity { log("ERR: Out of heap space!") return -1 } let id = nextId nextId++ heap.blocks[str(id)] = {size: size, id: id} heap.used += size log(`alloc(${size}) → block #${id} [${heap.used}/${heap.capacity} used]`) return id } func free(id) { let key = str(id) if not has(heap.blocks, key) { log("ERR: Block #" + id + " not found") return } heap.used -= heap.blocks[key].size delete heap.blocks[key] log(`free(#${id}) [${heap.used}/${heap.capacity} used]`) } func status() { let count = len(heap.blocks) log(`--- Heap: ${heap.used}/${heap.capacity} bytes, ${count} blocks ---`) } let a = alloc(8) let b = alloc(12) let c = alloc(6) status() free(a) free(b) let d = alloc(18) status()
BFS Graph Traversal
Breadth-First Search across a directed graph stored as an adjacency list. Demonstrates queues, object membership checks, and nested loops.
let graph = { A: ["B", "C"], B: ["D", "E"], C: ["F"], D: [], E: ["F"], F: [] } func bfs(start) { let visited = {} let queue = [start] let order = [] visited[start] = true while len(queue) > 0 { let node = queue[0] queue = slice(queue, 1) append(order, node) log("Visiting: " + node) for neighbor in graph[node] { if not has(visited, neighbor) { visited[neighbor] = true append(queue, neighbor) } } } return order } let path = bfs("A") log("BFS order: " + join(path, " → "))
Async Progress Simulation
Demonstrates the power of wait() in loops to create timed output sequences — useful for visualizing algorithms step-by-step.
func simulate(label, steps) { log(`[{label}] Starting...`) let i = 0 while i < steps { i++ let pct = round((i / steps) * 100) let bar = repeat("█", i) + repeat("░", steps - i) log(`[{label}] {bar} {pct}%`) wait(150) } log(`[{label}] Complete ✓`) } simulate("Compile", 6) simulate("Deploy", 4)