ucode is a lightweight scripting language used by OpenWrt with ECMAScript-like syntax. It's designed for system scripting, configuration management, and template processing in embedded Linux environments.
Key Characteristics:
- Synchronous, procedural programming model
- Built-in JSON parsing and serialization
- 64-bit integer support
- POSIX regular expression support
- Small footprint (~64KB on ARM Cortex A9)
- Reference counting memory management with optional mark-and-sweep GC
# Run a script
ucode script.uc
# Execute inline code
ucode -e "print('Hello World\n')"
# Print expression result
ucode -p "1 + 2"
# Compile to bytecode
ucode -c script.uc
# Template mode (Jinja-like)
ucode -T template.uc
# Enable garbage collection
ucode -g script.uc
# Strict mode
ucode -S script.uc- Boolean:
true,false - Integer: 64-bit signed integers (-9223372036854775808 to +9223372036854775807)
- Double: Floating-point numbers (-1.7e308 to +1.7e308)
- String:
'single quotes'or"double quotes"with escape sequences - Null:
null
- Array:
[1, 2, "three", true] - Object:
{ key: "value", "quoted-key": 123 }
// Global variable (no keyword)
globalVar = 100;
// Local variable (function/block scoped)
let localVar = 200;
// Constant (cannot be reassigned)
const CONSTANT_VAR = 300;
+, -, *, /, %, ++, --
&, |, ^, <<, >>, ~
==, !=, <, <=, >, >=, ===, !==
&&, ||, !, ?? (nullish coalescing)
=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
in: Check if key exists in object/arraytypeof: Get type as stringdelete: Remove object property...: Spread operator for arrays/objects
if (condition) {
// code
} else if (another_condition) {
// code
} else {
// code
}
// Ternary operator
let result = condition ? value1 : value2;
// While loop
while (condition) {
// code
}
// For loop
for (let i = 0; i < 10; i++) {
// code
}
// For...in (iterate over array/object)
for (item in array) {
print(item);
}
// For...in with destructuring
for (key, value in object) {
print(key, ": ", value);
}
// Do...while
do {
// code
} while (condition);
switch (expression) {
case value1:
// code
break;
case value2:
case value3:
// code for both cases
break;
default:
// default code
}
break: Exit loop or switchcontinue: Skip to next iterationreturn: Return from function
// Function declaration
function add(a, b) {
return a + b;
}
// Function expression
let multiply = function(a, b) {
return a * b;
};
// Arrow functions (limited support)
let square = (x) => x * x;
// Default parameters
function greet(name = "World") {
print("Hello, " + name);
}
// Rest parameters
function sum(...numbers) {
let total = 0;
for (n in numbers) {
total += n;
}
return total;
}
// Functions are first-class values
let func = add;
let result = func(1, 2);
type(value): Returns type as string ("null", "boolean", "number", "string", "array", "object", "function", "resource")length(value): Returns length of string, array, or objectexists(obj, key): Check if key exists in object
print(...args): Output to stdoutwarn(...args): Output to stderrprintf(format, ...args): Formatted output to stdoutsprintf(format, ...args): Return formatted string
push(arr, ...items): Add items to endpop(arr): Remove and return last itemshift(arr): Remove and return first itemunshift(arr, ...items): Add items to beginningslice(arr, start, end): Extract portionsplice(arr, start, deleteCount, ...items): Modify arraysort(arr, compareFn): Sort in placereverse(arr): Reverse in placejoin(arr, separator): Concatenate to stringindex(arr, value): Find first occurrencerindex(arr, value): Find last occurrenceuniq(arr): Remove duplicatesvalues(arr): Return array values (identity for arrays)
map(arr, fn): Transform each elementfilter(arr, fn): Filter elementsreduce(arr, fn, initial): Reduce to single value
keys(obj): Return array of keysvalues(obj): Return array of valuesdelete obj.keyordelete(obj, key): Remove property
substr(str, start, length): Extract substringsplit(str, separator, limit): Split into arrayjoin(arr, separator): Join array into stringtrim(str): Remove whitespace from both endsltrim(str): Remove leading whitespacertrim(str): Remove trailing whitespacelc(str): Convert to lowercaseuc(str): Convert to uppercaseindex(str, needle): Find substring positionrindex(str, needle): Find last occurrencereplace(str, search, replace, flags): Replace occurrencesmatch(str, regex): Match against regexord(str): Get character codechr(code): Get character from code
int(value): Convert to integermin(...values): Return minimummax(...values): Return maximumabs(value): Absolute valueround(value): Round to nearest integerfloor(value): Round downceil(value): Round up
json(jsonString): Parse JSON stringjsonstr(value): Serialize to JSON (compact)
match(str, pattern): Match string against regexreplace(str, pattern, replacement, flags): Replace with regex
gc(): Trigger garbage collectionrequire(moduleName): Load moduleinclude(filePath): Include and execute filesourcepath(): Get current source file path
- True contiguous memory arrays
- Support negative indexing (
arr[-1]for last element) - Can be sparse (gaps allocate memory)
- Mixed types allowed
// Creating arrays
let empty = [];
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "two", true, null, { key: "value" }];
// Accessing elements
let first = arr[0];
let last = arr[-1];
// Modifying
arr[0] = "new value";
arr[length(arr)] = "append"; // Add to end
// Checking existence
if (index in arr) {
// index exists
}
// Iteration
for (item in arr) {
print(item);
}
// With index
for (let i = 0; i < length(arr); i++) {
print(i, ": ", arr[i]);
}
- Ordered hash tables (preserves insertion order)
- O(1) average lookup time
- Keys must be strings (auto-conversion)
- Reference semantics
// Creating objects
let empty = {};
let person = {
name: "Alice",
age: 30,
"special-key": "value"
};
// Accessing properties
let name = person.name;
let name2 = person["name"];
let special = person["special-key"];
// Setting properties
person.email = "alice@example.com";
person["city"] = "New York";
// Checking existence
if ("name" in person) {
// property exists
}
if (exists(person, "name")) {
// alternative check
}
// Deleting properties
delete person.age;
// Iteration
for (key in person) {
print(key, ": ", person[key]);
}
// Destructuring iteration
for (key, value in person) {
printf("%s = %J\n", key, value);
}
- Automatic for primitives
- Arrays and objects use reference counting
- Variables going out of scope decrement count
- Memory freed when count reaches zero
// Cyclic reference - causes memory leak
let obj = {};
obj.self = obj; // Circular reference
// Array self-reference
let arr = [];
arr[0] = arr; // Circular reference
- Use
-gflag to enable mark-and-sweep GC - Call
gc()manually when needed - Break circular references explicitly
import { readfile, writefile, stat, mkdir } from 'fs';
import * as fs from 'fs';
// File operations
let content = fs.readfile('/path/to/file');
fs.writefile('/path/to/file', 'content');
fs.unlink('/path/to/file'); // Delete
fs.rename('old', 'new');
// Directory operations
fs.mkdir('/path/to/dir');
fs.rmdir('/path/to/dir');
let files = fs.lsdir('/path/to/dir');
fs.chdir('/new/working/dir');
// File information
let info = fs.stat('/path/to/file');
let isReadable = fs.access('/path/to/file', 'r');
fs.chmod('/path/to/file', 0o755);
fs.chown('/path/to/file', uid, gid);
// Special operations
fs.symlink('target', 'link');
let target = fs.readlink('link');
let handle = fs.open('file', 'r');
let pipe = fs.pipe();
let proc = fs.popen('command', 'r');
import { cursor } from 'uci';
// Create UCI context
let ctx = cursor();
// Read configuration
let value = ctx.get('config', 'section', 'option');
let firstValue = ctx.get_first('config', 'type', 'option');
let allValues = ctx.get_all('config', 'section');
// Write configuration
ctx.set('config', 'section', 'option', 'value');
ctx.add('config', 'type'); // Add new section
ctx.delete('config', 'section', 'option');
// Commit changes
ctx.save('config');
ctx.commit('config');
// Error handling
if (!ctx.set('invalid', 'test', '1')) {
print(ctx.error());
}
import * as uloop from 'uloop';
// Initialize event loop
uloop.init();
// Timers
let timer = uloop.timer(1000, function() {
print("Timer fired!");
return 1000; // Repeat after 1000ms
});
// Intervals
let interval = uloop.interval(500, function() {
print("Every 500ms");
});
// Process handling
let proc = uloop.process("/bin/ls", ["-l"], null, function(code) {
printf("Process exited: %d\n", code);
});
// Signal handling
let sig = uloop.signal("SIGINT", function() {
print("SIGINT received");
uloop.end();
});
// File descriptor monitoring
let handle = uloop.handle(fd, function(events) {
if (events & uloop.ULOOP_READ) {
// Handle readable
}
}, uloop.ULOOP_READ);
// Run event loop
uloop.run();
// Cleanup
timer.cancel();
interval.cancel();
handle.delete();
When using -T flag, ucode processes files as templates with Jinja-like syntax:
{# This is a comment #}
{% for item in items %}
{{ item.name }}: {{ item.value }}
{% endfor %}
{% if condition %}
{{ variable }}
{% else %}
Default value
{% endif %}
{%+ include "other_template.uc" %}
- Variable Scoping: Always use
letfor local variables to avoid polluting global scope - Error Handling: Check return values and use error functions
- Memory Management: Be aware of circular references, use GC when needed
- Type Checking: Use
type()function to validate inputs - String Formatting: Use
sprintf()orprintf()for complex formatting - Arrays vs Objects: Use arrays for ordered collections, objects for key-value mappings
- Module Loading: Use
importfor modules,include()for scripts - Performance: Cache object property access in loops
- Template Processing: Use template mode for generating configuration files
function getProp(obj, path, defaultValue) {
let parts = split(path, '.');
let current = obj;
for (part in parts) {
if (type(current) != "object" || !(part in current)) {
return defaultValue;
}
current = current[part];
}
return current;
}
function deepClone(obj) {
if (type(obj) != "object" && type(obj) != "array") {
return obj;
}
let clone = type(obj) == "array" ? [] : {};
for (key, value in obj) {
clone[key] = deepClone(value);
}
return clone;
}
function intersect(...arrays) {
if (!length(arrays)) return [];
let result = arrays[0];
for (let i = 1; i < length(arrays); i++) {
result = filter(result, item => item in arrays[i]);
}
return uniq(result);
}
function merge(...objects) {
let result = {};
for (obj in objects) {
for (key, value in obj) {
result[key] = value;
}
}
return result;
}
- Documentation source: https://ucode.mein.io
- Primary use case: OpenWrt system scripting and configuration
- Language inspiration: ECMAScript syntax with Perl-like built-ins
- No object-oriented features (no classes, prototypes, or this keyword)
- Synchronous execution only (no async/await or promises)