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 valuesnumber- Numeric valuesboolean- true/falsenull- Empty valueundefined- Uninitialized
Complex Types
object- Key-value pairsarray- Ordered listsfunction- Callable codeclass- 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();