Natural-Language RelScript is the friendly authoring level of the same language contract as Core RelScript. Both lower to the same typed operations; tagged language examples on this page are compatibility reference material for existing apps.

RelScript
System Language for Polished Apps

A Ravady-native language that borrows modern scripting ergonomics while targeting deterministic runtime services, packaged capabilities, and polished application surfaces.

// Hello World in RelScript
import { readDirectory, notify } from '@ravady/system';

async function greetWorkspace(path) {
    const files = await readDirectory(path);
    notify({ title: 'Workspace Ready', body: `${files.length} files indexed` });
    return `Hello from RelScript.`;
}

print(await greetWorkspace('/workspace'));

Getting Started

Easy Installation

RelScript ships as part of the Ravady developer toolchain and runtime workflow.

Familiar Syntax

Modern syntax that feels approachable without pretending the system runtime is a browser.

Installation

# Verify the bundled RelScript toolchain
rel --version

# Compile or run a package entry
rel hello.rel

Hello World

Create a file called hello.rel:

// hello.rel
function main() {
    print("Hello, RAVADY OS!");
    print("Welcome to RelScript!");
}

main();

Run it:

rel hello.rel

Language Guide

Syntax Basics

// Variables
let name = "RelScript";
const version = "1.0";

// Functions
function greet(person) {
    return `Hello, ${person}!`;
}

// Arrow functions
const square = (x) => x * x;

// Classes
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    area() {
        return this.width * this.height;
    }
}

// Async/await
async function fetchData() {
    const response = await fetch('/api/data');
    return await response.json();
}

Variables & Types

Primitive Types

  • string - Text values
  • number - Numeric values
  • boolean - true/false
  • null - Empty value
  • undefined - Uninitialized

Complex Types

  • object - Key-value pairs
  • array - Ordered lists
  • function - Callable code
  • class - Object templates

Functions

// Function declaration
function add(a, b) {
    return a + b;
}

// Function expression
const multiply = function(a, b) {
    return a * b;
};

// Arrow function
const divide = (a, b) => a / b;

// Default parameters
function greet(name = "World") {
    return `Hello, ${name}!`;
}

// Rest parameters
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

// Higher-order functions
function applyOperation(a, b, operation) {
    return operation(a, b);
}

const result = applyOperation(5, 3, (x, y) => x * y);

Classes & Objects

// Class definition
class Animal {
    constructor(name, species) {
        this.name = name;
        this.species = species;
    }

    speak() {
        return `${this.name} makes a sound!`;
    }

    static createDog(name) {
        return new Animal(name, "dog");
    }
}

// Inheritance
class Dog extends Animal {
    constructor(name, breed) {
        super(name, "dog");
        this.breed = breed;
    }

    speak() {
        return `${this.name} barks!`;
    }

    fetch() {
        return `${this.name} fetches the ball!`;
    }
}

// Usage
const dog = new Dog("Buddy", "Golden Retriever");
print(dog.speak()); // "Buddy barks!"
print(dog.fetch()); // "Buddy fetches the ball!"

Async Programming

RelScript keeps asynchronous work clear and predictable while respecting the permissions chosen for each app.

// Async function
async function fetchUserData(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        return userData;
    } catch (error) {
        console.error("Failed to fetch user data:", error);
        throw error;
    }
}

// Promise-based approach
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function example() {
    print("Start");
    await delay(1000);
    print("After 1 second");
    await delay(2000);
    print("After another 2 seconds");
    print("Done");
}

// Parallel execution
async function fetchMultipleUsers(userIds) {
    const promises = userIds.map(id => fetchUserData(id));
    const users = await Promise.all(promises);
    return users;
}

Modules

math.rel

// math.rel
export function add(a, b) {
    return a + b;
}

export function multiply(a, b) {
    return a * b;
}

export const PI = 3.14159;

main.rel

// main.rel
import { add, multiply, PI } from './math';

print(add(5, 3));        // 8
print(multiply(4, 2));   // 8
print(PI);               // 3.14159

API Reference

Standard Library

Console

  • console.log()
  • console.error()
  • console.warn()

Math

  • Math.abs()
  • Math.round()
  • Math.random()

Date

  • new Date()
  • Date.now()
  • date.toISOString()

File System API

import { readFile, writeFile, readdir } from 'fs';

// Read a file
const content = await readFile('example.txt', 'utf8');
print(content);

// Write to a file
await writeFile('output.txt', 'Hello, RAVADY OS!');

// List directory contents
const files = await readdir('.');
files.forEach(file => print(file));

Networking API

import { createServer, request } from 'http';

// Simple HTTP server
const server = createServer((req, res) => {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello from RelScript!\n');
});

server.listen(3000, () => {
    print('Server running on port 3000');
});

// HTTP request
const response = await request('https://api.example.com/data');
const data = await response.json();
print(data);

GUI API

RelScript brings familiar controls and polished surfaces together through Ravady's WebNative interface model. See the Runtime Bridge page for the composition model.

import { Window, Button, TextField } from 'gui';

// Create a window
const window = new Window({
    title: 'My App',
    width: 400,
    height: 300
});

// Add controls
const button = new Button('Click me!');
button.onClick = () => {
    print('Button clicked!');
};

const textField = new TextField();
textField.placeholder = 'Enter text...';

window.addChild(button);
window.addChild(textField);
window.show();

Why RelScript

RelScript feels familiar to modern developers, but it is designed for Ravady's package, permission, and UI model.

What Feels Familiar

  • Syntax: Modern scripting patterns with concise, readable structure
  • Features: Classes, modules, destructuring, spread, async syntax, and expressive composition through the Lua-based runtime
  • Paradigm: Multi-paradigm (functional, object-oriented, procedural)
  • Modules: Clear import and package boundaries

What Changes

  • Runtime: Lua VM vs V8/Node.js
  • Platform: Ravady applications, services, and packaged tools first
  • APIs: Capability-gated system services instead of browser-first globals
  • Performance: Efficient runtime targets with tighter system integration

Detailed Positioning

Aspect JavaScript Experience RelScript Focus
Primary Runtime V8, SpiderMonkey, JavaScriptCore Lua 5.4 VM (compiled target)
Target Platform Web browsers, Node.js, cross-platform apps RAVADY OS apps, services, and packaged tools
Standard Library DOM APIs, Node.js APIs, Web APIs RAVADY OS services, file system, GUI, launcher permissions
Performance Focus JIT compilation, web optimization Lua VM efficiency, native performance
Package Ecosystem npm and browser ecosystems RAVADY package manager with compatibility bridges where useful
Memory Management Garbage collection (V8) Lua garbage collector
Concurrency Single-threaded with Web Workers Lua coroutines + OS threads
Compilation JIT at runtime Ahead-of-time to Lua bytecode

Why Choose RelScript?

  • Native Performance: Lua's efficiency for desktop apps
  • Familiar Syntax: JavaScript-like without learning curve
  • OS Integration: Direct access to RAVADY OS features
  • Lightweight: Small runtime, fast startup
  • Modern Features: Classes, modules, destructuring, spread, and async syntax

Migration from JavaScript

// JavaScript (Browser)
fetch('/api/data')
  .then(res => res.json())
  .then(data => console.log(data));

// RelScript (RAVADY OS)
const response = await fetch('/api/data');
const data = await response.json();
print(data);

RelScript is RAVADY OS's native programming language. Use WebNative APIs for GUI and system integration.

Examples

Desktop Application

import { Window, Button, Label, TextField } from 'gui';

class CalculatorApp {
    constructor() {
        this.window = new Window({
            title: 'Calculator',
            width: 300,
            height: 400
        });

        this.display = new TextField();
        this.display.text = '0';
        this.display.y = 20;

        this.createButtons();
        this.setupEventHandlers();
    }

    createButtons() {
        const buttons = [
            '7', '8', '9', '+',
            '4', '5', '6', '-',
            '1', '2', '3', '*',
            '0', '.', '=', '/'
        ];

        let x = 20, y = 80;
        buttons.forEach((text, index) => {
            const button = new Button(text);
            button.x = x;
            button.y = y;
            button.width = 50;
            button.height = 50;

            this.window.addChild(button);

            x += 60;
            if ((index + 1) % 4 === 0) {
                x = 20;
                y += 60;
            }
        });
    }

    setupEventHandlers() {
        // Calculator state
        this.currentInput = '0';
        this.previousInput = '';
        this.operation = null;
        this.waitingForNewValue = false;

        // Get all buttons from the window
        const buttons = this.window.children.filter(child => child.constructor.name === 'Button');

        buttons.forEach(button => {
            button.onClick = () => {
                this.handleButtonClick(button.text);
            };
        });
    }

    handleButtonClick(buttonText) {
        switch (buttonText) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                this.handleNumber(buttonText);
                break;
            case '+':
            case '-':
            case '*':
            case '/':
                this.handleOperator(buttonText);
                break;
            case '=':
                this.handleEquals();
                break;
            case '.':
                this.handleDecimal();
                break;
            case 'C':
                this.handleClear();
                break;
            default:
                // Handle any other buttons if added later
                break;
        }
        this.updateDisplay();
    }

    handleNumber(number) {
        if (this.waitingForNewValue) {
            this.currentInput = number;
            this.waitingForNewValue = false;
        } else {
            if (this.currentInput === '0') {
                this.currentInput = number;
            } else {
                this.currentInput += number;
            }
        }
    }

    handleOperator(operator) {
        const inputValue = parseFloat(this.currentInput);

        if (this.previousInput === '' || this.waitingForNewValue) {
            this.previousInput = this.currentInput;
        } else if (this.operation) {
            const result = this.calculate(parseFloat(this.previousInput), inputValue, this.operation);
            this.previousInput = result.toString();
            this.currentInput = result.toString();
        }

        this.waitingForNewValue = true;
        this.operation = operator;
    }

    handleEquals() {
        const inputValue = parseFloat(this.currentInput);

        if (this.operation && this.previousInput !== '') {
            const result = this.calculate(parseFloat(this.previousInput), inputValue, this.operation);
            this.currentInput = result.toString();
            this.previousInput = '';
            this.operation = null;
            this.waitingForNewValue = true;
        }
    }

    handleDecimal() {
        if (this.waitingForNewValue) {
            this.currentInput = '0.';
            this.waitingForNewValue = false;
        } else if (this.currentInput.indexOf('.') === -1) {
            this.currentInput += '.';
        }
    }

    handleClear() {
        this.currentInput = '0';
        this.previousInput = '';
        this.operation = null;
        this.waitingForNewValue = false;
    }

    calculate(firstValue, secondValue, operation) {
        switch (operation) {
            case '+':
                return firstValue + secondValue;
            case '-':
                return firstValue - secondValue;
            case '*':
                return firstValue * secondValue;
            case '/':
                if (secondValue === 0) {
                    return 0; // Handle division by zero
                }
                return firstValue / secondValue;
            default:
                return secondValue;
        }
    }

    updateDisplay() {
        // Format the display value
        let displayValue = this.currentInput;

        // Handle large numbers
        if (displayValue.length > 12) {
            displayValue = parseFloat(displayValue).toExponential(5);
        }

        this.display.text = displayValue;
    }

    run() {
        this.window.show();
    }
}

// Run the application
const app = new CalculatorApp();
app.run();

Web Server

import { createServer } from 'http';
import { readFile } from 'fs';

const server = createServer(async (req, res) => {
    const url = req.url;

    if (url === '/') {
        // Serve HTML page
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(`
            <!DOCTYPE html>
            <html>
            <head><title>RelScript Server</title></head>
            <body>
                <h1>Hello from RelScript!</h1>
                <p>Server time: ${new Date().toISOString()}</p>
            </body>
            </html>
        `);
    } else if (url === '/api/time') {
        // JSON API endpoint
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
            timestamp: Date.now(),
            iso: new Date().toISOString(),
            timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
        }));
    } else {
        // 404 Not Found
        res.writeHead(404, { 'Content-Type': 'text/plain' });
        res.end('Page not found');
    }
});

const PORT = 8080;
server.listen(PORT, () => {
    print(`Server running at http://localhost:${PORT}`);
    print('Press Ctrl+C to stop');
});

File Manager

import { Window, ListView, Button, Label } from 'gui';
import { readdir, stat, mkdir, unlink } from 'fs';
import { join } from 'path';

class FileManager {
    constructor() {
        this.currentPath = '.';
        this.window = new Window({
            title: 'File Manager',
            width: 800,
            height: 600
        });

        this.createUI();
        this.loadDirectory();
    }

    createUI() {
        // Path display
        this.pathLabel = new Label(`Current: ${this.currentPath}`);
        this.pathLabel.y = 10;
        this.window.addChild(this.pathLabel);

        // File list
        this.fileList = new ListView();
        this.fileList.y = 40;
        this.fileList.width = 780;
        this.fileList.height = 500;
        this.window.addChild(this.fileList);

        // Buttons
        const refreshBtn = new Button('Refresh');
        refreshBtn.x = 10;
        refreshBtn.y = 550;
        refreshBtn.onClick = () => this.loadDirectory();
        this.window.addChild(refreshBtn);

        const newFolderBtn = new Button('New Folder');
        newFolderBtn.x = 100;
        newFolderBtn.y = 550;
        newFolderBtn.onClick = () => this.createNewFolder();
        this.window.addChild(newFolderBtn);
    }

    async loadDirectory() {
        try {
            const items = await readdir(this.currentPath);
            const fileItems = [];

            for (const item of items) {
                const fullPath = join(this.currentPath, item);
                const stats = await stat(fullPath);

                fileItems.push({
                    name: item,
                    type: stats.isDirectory() ? 'folder' : 'file',
                    size: stats.size,
                    modified: stats.mtime.toISOString()
                });
            }

            this.fileList.items = fileItems;
            this.pathLabel.text = `Current: ${this.currentPath}`;
        } catch (error) {
            print(`Error loading directory: ${error.message}`);
        }
    }

    async createNewFolder() {
        const folderName = 'New Folder';
        const folderPath = join(this.currentPath, folderName);

        try {
            await mkdir(folderPath);
            this.loadDirectory();
        } catch (error) {
            print(`Error creating folder: ${error.message}`);
        }
    }

    run() {
        this.window.show();
    }
}

// Run the file manager
const fm = new FileManager();
fm.run();