Welcome to Xel, a dynamic, modern scripting language built for simplicity and power. Xel is implemented in Go (powered by VirtLang-Go v2 engine) and offers a familiar syntax with robust features.
- Overview
- Getting Started
- Language Basics
- Control Flow
- Functions
- Classes and Objects
- Error Handling (
try,catch) - Modules and Imports
- Built-in Globals and Functions
- Standard Library
- Command-Line Interface (CLI)
Xel is designed to be an easy-to-learn scripting language that is both expressive and efficient. It supports procedural, object-oriented, and functional programming paradigms.
Key features include:
- Dynamic Typing
- Lexical Scoping and Closures
- Classes with Public/Private Members
- A Simple Module System
- Built-in Data Structures (Objects, Arrays)
- Comprehensive Error Handling
- A Growing Standard Library
- An Interactive REPL
Xel provides an update script for easy installation and updates:
curl -fsSL https://raw.githubusercontent.com/dev-kas/xel/master/scripts/install.sh | shThe runtime will also notify you if a new version is available when you run it.
To execute a Xel script file (which should have a .xel extension):
xel run your_script.xel [arg1 arg2 ...]Any arguments passed after the script name will be available within the script via the proc.args array.
Example:
your_script.xel:
print("Script name:", __filename__)
print("Arguments:", proc.args)
Running xel run your_script.xel hello world would output:
Script name: /path/to/your_script.xel
Arguments: ["hello", "world"]
Running xel without any arguments starts the Read-Eval-Print Loop (REPL):
xelThis provides an interactive environment to experiment with Xel code.
- Type Xel expressions or statements and press Enter.
- Use
!exitto quit the REPL. - Command history is available (use Up/Down arrows).
Xel supports two types of comments:
- Single-line comments:
// This is a comment - Multi-line comments:
/* This is a multi-line comment */
Xel is case-sensitive. myVariable and myvariable are treated as two different identifiers.
Multiple statements or expressions can be simply separated by a space. They are automatically distinguished appropriately by the parser. They can be used to separate multiple statements on a single line.
let a = 1 let b = 2 // Space separating two statements
print(a + b)
Xel is dynamically typed. The main data types are:
- Number: Represents both integers and floating-point numbers (internally
float64).let integer = 10 let floatNum = 3.14 - String: A sequence of characters. Can be defined using single (
') or double (") quotes.let greeting = "Hello, Xel!" let name = 'World' - Boolean: Represents
trueorfalse.let isActive = true let isDone = false - Nil: Represents the absence of a value.
let noValue = nil - Object: An unordered collection of key-value pairs (like dictionaries or maps). Keys are strings.
let person = { name: "Jedi", age: 30 } - Array: An ordered list of values.
let numbers = [1, 2, 3, 4] let mixed = [1, "two", true, nil] - Function: A block of code that can be called.
- Class: A blueprint for creating objects.
- ClassInstance: An object created from a class.
- Variables (
let): Used to declare mutable variables.let message = "Hello" message = "Hi" // Allowed - Constants (
const): Used to declare immutable variables. Once assigned, their value cannot be changed.const PI = 3.14159 // PI = 3.14 // This would cause an error
Scope is lexical (block-scoped).
Xel supports a standard set of operators:
-
Arithmetic Operators:
+(Addition, String Concatenation)-(Subtraction)*(Multiplication)/(Division)%(Modulo) Precedence follows standard mathematical rules (e.g.,*and/before+and-). Use parentheses()to control evaluation order.
let sum = 5 + 3 // 8 let product = 4 * 2 // 8 let result = (2 + 3) * 4 // 20 let greeting = "Hello" + " " + "World" // "Hello World" -
Comparison Operators:
==(Equal to)!=(Not equal to)<(Less than)>(Greater than)<=(Less than or equal to)>=(Greater than or equal to) These operators evaluate to a boolean (trueorfalse). Currently, they primarily operate on numbers.
let isEqual = (5 == 5) // true let isGreater = (10 > 3) // true -
Assignment Operator:
=(Assign value)
let x = 10 x = 20 // x is now 20 -
Member Access Operators:
.(Dot notation for object properties):myObject.property[](Bracket notation for object properties and array elements):myObject["property"],myArray[0]
Xel uses if, else if, and else for conditional execution.
let num = 10
if (num > 10) {
print("Number is greater than 10")
} else if (num < 10) {
print("Number is less than 10")
} else {
print("Number is exactly 10")
}
The condition in parentheses is evaluated. If "truthy", the corresponding block is executed.
- Truthy values:
true, non-zero numbers, non-empty strings, objects, arrays, functions. - Falsy values:
false,0,""(empty string),nil.
Xel provides a while loop for repeated execution of a block of code as long as a condition is true.
let count = 0
while (count < 5) {
print(count)
count = count + 1
}
// Output: 0, 1, 2, 3, 4
break: Exits the innermostwhileloop immediately.let i = 0 while (true) { if (i == 3) { break // Exit loop when i is 3 } print(i) i = i + 1 } // Output: 0, 1, 2continue: Skips the rest of the current iteration and proceeds to the next iteration of the innermostwhileloop.let i = 0 while (i < 5) { i = i + 1 if (i == 3) { continue // Skip printing 3 } print(i) } // Output: 1, 2, 4, 5
Functions are first-class citizens in Xel. They can be assigned to variables, passed as arguments, and returned from other functions.
Use the fn keyword to define a function.
fn greet(name) {
return "Hello, " + name + "!"
}
let message = greet("Xel") // "Hello, Xel!"
print(message)
Functions can also be defined without a name (anonymously) and assigned to variables.
let add = fn(a, b) {
return a + b
}
let sum = add(5, 3) // 8
print(sum)
Anonymous functions are often used for callbacks or Immediately Invoked Function Expressions (IIFEs), though Xel doesn't require explicit IIFE syntax for simple expressions.
Xel supports lexical scoping and closures. A function can access variables from its containing (enclosing) scopes, even after the outer function has finished executing.
fn outer(x) {
fn inner(y) {
return x + y // 'x' is captured from outer's scope
}
return inner
}
let add5 = outer(5)
let result = add5(3) // 8
print(result)
Functions use the return keyword to send a value back to the caller. If return is omitted or used without a value, the function implicitly returns nil.
fn getNumber() {
return 42
}
fn doNothing() {
// implicitly returns nil
}
print(getNumber()) // 42
print(doNothing()) // nil
Xel supports object-oriented programming with classes.
Use the class keyword to define a class.
class Rectangle {
public width
public height
public constructor(w, h) {
width = w
height = h
}
public area() {
return width * height
}
private describe() { // Private method
return "Rectangle: " + width + "x" + height
}
public getDetails() {
return describe() // Can call private method from within the class
}
}
A special method named constructor is called when a new instance of the class is created. It's used to initialize the object's properties. If no explicit constructor is provided, a default one is assumed.
Properties and methods defined directly in the class body (not inside constructor) are also part of the class definition.
- Properties: Variables associated with an object (e.g.,
width,height). - Methods: Functions associated with an object that can operate on its data (e.g.,
area()).
Class members (properties and methods) can be declared as public or private.
public: Accessible from anywhere. This is the default if no access modifier is specified (though explicitly usingpublicis good practice).private: Accessible only from within the class itself.class MyClass { public publicVar private privateVar public constructor(val) { publicVar = val privateVar = val * 2 } public getPrivate() { return privateVar // Accessible } } let obj = MyClass(10) print(obj.publicVar) // 10 print(obj.privateVar) // nil (cannot access privateVar from outside) print(obj.getPrivate()) // 20
Create an instance of a class by calling the class name as if it were a function, passing arguments to its constructor.
let rect = Rectangle(10, 5)
print(rect.width) // 10
print(rect.area()) // 50
print(rect.getDetails()) // Rectangle: 10x5
// print(rect.describe()) // This would result in an error or nil, as describe is private
You can create objects directly using object literal syntax:
let car = {
make: "XelMotors",
model: "SedanX",
year: 2024,
start: fn() {
print(make + " " + model + " started.")
}
}
print(car.make) // XelMotors
car.start() // XelMotors SedanX started.
// Accessing properties
print(car["model"]) // SedanX
// Adding new properties
car.color = "blue"
print(car.color) // blue
// Modifying properties
car.year = 2025
Property shorthand is also supported if a variable with the same name exists in the current scope:
let name = "Jedi"
let age = 30
let user = { name, age } // equivalent to { name: name, age: age }
print(user.name) // Jedi
Arrays are ordered collections of items:
let numbers = [10, 20, 30]
let colors = ["red", "green", "blue"]
print(numbers[0]) // 10
print(colors[1]) // "green"
colors[1] = "yellow" // Modify element
print(colors) // ["red", "yellow", "blue"]
// Adding elements (though `xel:array.push` is preferred for clarity)
colors[3] = "purple"
print(colors) // ["red", "yellow", "blue", "purple"]
print(len(colors)) // 4
Xel provides try and catch blocks for handling runtime errors.
try {
// Code that might throw an error
let result = riskyOperation()
print("Operation successful:", result)
} catch e {
// Code to handle the error
print("An error occurred:", e) // 'e' will contain the error message string
}
If an error occurs within the try block, execution jumps to the catch block. The variable specified after catch (e.g., e) will hold a string describing the error.
Xel supports a module system for organizing code into reusable units.
Use the import() function to load modules.
- Relative file paths:
// Assuming myModule.xel is in the same directory or a subdirectory let myMod = import("./myModule") let otherMod = import("./lib/otherModule") - Native Xel modules:
let math = import("xel:math") let strings = import("xel:strings")
The import() function returns the exports object from the imported module. Xel caches imported modules, so subsequent imports of the same module will return the cached instance. Cycle detection is in place to prevent infinite loops from circular dependencies.
To expose functionality from a module file, Simply return the value as the last expression. This value becomes the value returned by import().
Example myMath.xel:
fn add(a, b) {
return a + b
}
fn subtract(a, b) {
return a - b
}
// This object will be what's returned when importing myMath.xel
return {
adder: add,
subtractor: subtract,
PI: 3.14159
}
Example main.xel:
let myMath = import("./myMath")
print(myMath.PI) // 3.14159
print(myMath.adder(5, 3)) // 8
Xel comes with pre-built native modules (implemented in Go) for common tasks. These are imported using the xel: prefix. See the Standard Library section.
Xel provides several global constants and functions:
true: The boolean true value.false: The boolean false value.nil: The nil value.NaN: Not-a-Number.inf: Infinity.
print(...args): Prints its arguments to the console, space-separated.print("Hello", "Xel", 2024) // Hello Xel 2024len(value): Returns the length of a string (character count) or an array (element count).print(len("hello")) // 5 print(len([1, 2, 3])) // 3typeof(value): Returns a string representing the type of the value.print(typeof(10)) // "number" print(typeof("xel")) // "string" print(typeof(true)) // "boolean" print(typeof(nil)) // "nil" print(typeof({})) // "object" print(typeof([])) // "array" print(typeof(fn(){})) // "function"import(pathOrName): Loads a module. (See Modules and Imports)
These variables are automatically available in scripts run via xel run:
__filename__: A string containing the absolute path to the currently executing script file.__dirname__: A string containing the absolute path to the directory of the currently executing script file.proc: An object containing process-related information:proc.args: An array of strings representing command-line arguments passed to the script.proc.runtime_version: String, the version of the Xel runtime.proc.engine_version: String, the version of the VirtLang-Go engine.
Xel includes a standard library accessible via native modules.
Provides mathematical functions and constants.
let math = import("xel:math")
print(math.PI)
print(math.sqrt(16)) // 4
print(math.random()) // A random float between 0.0 (inclusive) and 1.0 (exclusive)
print(math.random(10)) // A random float between 0.0 and 10.0
print(math.random(5, 10))// A random float between 5.0 and 10.0
print(math.max(1, 5, 2)) // 5
print(math.abs(-10)) // 10
// ... and many more (sin, cos, floor, ceil, pow, log, etc.)
Some notable functions: abs, round, floor, ceil, sign, sin, cos, tan, trunc, sum (for arrays), mean (for arrays), median (for arrays), random, degToRad, radToDeg, atan, atan2, acos, asin, exp, log, log2, log10, pow, sqrt, cbrt, clamp, max, min. Constants: PI, E.
Provides functions for string manipulation.
let strings = import("xel:strings")
let s = " Hello Xel! "
print(strings.trim(s)) // "Hello Xel!"
print(strings.upper("hello")) // "HELLO"
print(strings.includes("Xel World", "Xel")) // true
print(strings.split("a,b,c", ",")) // ["a", "b", "c"]
print(strings.charAt("Xel", 0)) // "X"
print(strings.slice("XelLang", 0, 3)) // "Xel"
print(strings.replace("foo bar foo", "foo", "baz")) // "baz bar foo"
print(strings.format("Name: %v, Age: %v", "Jedi", 30)) // "Name: Jedi, Age: 30"
// ... and many more
Includes: charAt, charCodeAt, includes, startsWith, endsWith, indexOf, lastIndexOf, concat, slice, substring, substr, lower, upper, trim, trimStart, trimEnd, padStart, padEnd, repeat, replace, replaceAll, split, toArray, format.
Provides functions for array manipulation.
let array = import("xel:array")
let nums = [1, 2, 3, 4]
let doubled = array.map(nums, fn(x) { return x * 2 })
print(doubled) // [2, 4, 6, 8]
let evens = array.filter(nums, fn(x) { return x % 2 == 0 })
print(evens) // [2, 4]
let sum = array.reduce(nums, fn(acc, x) { return acc + x }, 0)
print(sum) // 10
print(array.includes(nums, 3)) // true
nums = array.push(nums, 5, 6) // Returns the new array: [1, 2, 3, 4, 5, 6]
print(nums)
// ... and many more
Includes: push, pop, shift, unshift, slice, splice, fill, reverse, sort, map, filter, forEach, reduce, reduceRight, includes, indexOf, lastIndexOf, find, findIndex, every, some, join, concat, from, of, create.
The xel executable provides the following commands:
xel run <filepath.xel> [args...]: Executes the specified Xel script.xel init: (Currently Unimplemented) Intended to initialize a new Xel project.xel install: (Currently Unimplemented) Intended to install Xel packages.xel: Starts the REPL if no command is given.xel --version: Displays the Xel runtime and VirtLang engine versions.xel --help: Displays help information.