A scripting language interpreter with a REPL interface and built-in modules for math, strings, JSON, time, arrays, and more.
Install RobinPath as a dependency in your project:
npm i @wiredwp/robinpathImport and create a RobinPath instance to execute scripts in your application:
import { RobinPath } from '@wiredwp/robinpath';
// Create an interpreter instance
const rp = new RobinPath();
// Execute a script
const result = await rp.executeScript(`
add 10 20
multiply $ 2
`);
console.log('Result:', result); // 60Use executeLine() for REPL-like behavior where state persists between calls:
const rp = new RobinPath();
// First line - sets $result
await rp.executeLine('$result = add 10 20');
console.log(rp.getLastValue()); // 30
// Second line - uses previous result
await rp.executeLine('multiply $result 2');
console.log(rp.getLastValue()); // 60Get and set variables programmatically:
const rp = new RobinPath();
// Set a variable from JavaScript
rp.setVariable('name', 'Alice');
rp.setVariable('age', 25);
// Execute script that uses the variable
await rp.executeScript(`
log "Hello" $name
log "Age:" $age
`);
// Get a variable value
const name = rp.getVariable('name');
console.log(name); // "Alice"Create isolated execution contexts with threads:
const rp = new RobinPath({ threadControl: true });
// Create a new thread
const thread1 = rp.createThread('user-123');
await thread1.executeScript('$count = 10');
// Create another thread with separate variables
const thread2 = rp.createThread('user-456');
await thread2.executeScript('$count = 20');
// Each thread maintains its own state
console.log(thread1.getVariable('count')); // 10
console.log(thread2.getVariable('count')); // 20
// Switch between threads
rp.useThread('user-123');
console.log(rp.currentThread?.getVariable('count')); // 10Extend RobinPath with your own builtin functions:
const rp = new RobinPath();
// Register a simple builtin
rp.registerBuiltin('greet', (args) => {
const name = String(args[0] ?? 'World');
return `Hello, ${name}!`;
});
// Use it in scripts
await rp.executeScript('greet "Alice"');
console.log(rp.getLastValue()); // "Hello, Alice!"Create and register custom modules:
const rp = new RobinPath();
// Register module functions
rp.registerModule('myapp', {
process: (args) => {
const data = args[0];
// Process data...
return processedData;
},
validate: (args) => {
const input = args[0];
return isValid(input);
}
});
// Register function metadata for documentation
rp.registerModuleFunctionMeta('myapp', 'process', {
description: 'Processes input data',
parameters: [
{
name: 'data',
dataType: 'object',
description: 'Data to process',
formInputType: 'json',
required: true
}
],
returnType: 'object',
returnDescription: 'Processed data'
});
// Register module-level metadata
rp.registerModuleInfo('myapp', {
description: 'Custom application module',
methods: ['process', 'validate']
});
// Use in scripts
await rp.executeScript(`
use myapp
myapp.process $data
`);Query available commands for autocomplete or help:
const rp = new RobinPath();
const commands = rp.getAvailableCommands();
console.log(commands.native); // Native commands (if, def, etc.)
console.log(commands.builtin); // Root-level builtins
console.log(commands.modules); // Available modules
console.log(commands.moduleFunctions); // Module.function names
console.log(commands.userFunctions); // User-defined functionsGet the AST with execution state for debugging or visualization:
const rp = new RobinPath({ threadControl: true });
const thread = rp.createThread('debug');
const script = `
add 5 5
$result = $
if $result > 5
multiply $result 2
endif
`;
const astResult = await thread.getASTWithState(script);
console.log(astResult.ast); // AST with lastValue at each node
console.log(astResult.variables); // Thread and global variables
console.log(astResult.lastValue); // Final result
console.log(astResult.callStack); // Call stack framesCheck if a script needs more input (useful for multi-line input):
const rp = new RobinPath();
const check1 = rp.needsMoreInput('if $x > 5');
console.log(check1); // { needsMore: true, waitingFor: 'endif' }
const check2 = rp.needsMoreInput('if $x > 5\n log "yes"\nendif');
console.log(check2); // { needsMore: false }Handle errors from script execution:
const rp = new RobinPath();
try {
await rp.executeScript('unknown_function 123');
} catch (error) {
console.error('Script error:', error.message);
// "Unknown function: unknown_function"
}Install globally to use the robinpath command:
npm i -g @wiredwp/robinpathOr use it directly with npx:
npx @wiredwp/robinpathStart the interactive REPL:
robinpathOr if installed locally:
npm run cliThis will start an interactive session where you can type commands and see results immediately.
helpor.help- Show help messageexit,quit,.exit,.quit- Exit the REPLclearor.clear- Clear the screen..- Show all available commands as JSON
Multi-line Blocks: The REPL automatically detects incomplete blocks and waits for completion:
> if $x > 5
... log "yes"
... endif
Backslash Line Continuation:
Use \ at the end of a line to continue the command on the next line:
> log "this is a very long message " \
... "that continues on the next line"
The backslash continuation works with any statement type and can be chained across multiple lines.
Thread Management: When thread control is enabled, the prompt shows the current thread and module:
default@math> add 5 5
10
default@math> use clear
Cleared module context
default> thread list
Threads:
- default (current)
- user-123
default> thread use user-123
Switched to thread: user-123
user-123>
Module Context:
The prompt shows the current module when using use:
> use math
Using module: math
default@math> add 5 5
10
default@math> use clear
Cleared module context
default>
Commands are executed by typing the command name followed by arguments:
add 10 20
log "Hello, World!"
multiply 5 3
Variables are prefixed with $:
$name = "Alice"
$age = 25
log $name $age
You can declare variables with var (mutable) or const (immutable):
var $count 0
const $MAX_RETRIES 5
Use $ to reference the last computed value:
add 10 20
multiply $ 2 # Uses 30 (result of add)
log $ # Prints 60
Assign the last value to a variable by simply referencing it:
add 5 3
$sum # Assigns 8 to $sum
log $sum # Prints 8
Assign the value of one variable to another, including chained assignments:
$city = "New York"
$city2 = $city # Copies "New York" to $city2
log $city2 # Prints "New York"
$number1 = 42
$number2 = $number1 # Copies 42 to $number2
$number3 = $number2 # Can chain assignments
Use into $variable to assign the result of a command, block, or function call directly to a variable:
math.add 10 20 into $sum
log $sum # Prints 30
# Works with functions
myFunction(1 2) into $result
# Works with do blocks
do into $blockResult
math.add 5 5
math.multiply $ 2
enddo
# Works with repeat loops
repeat 3 with into $total
add $2 10
endwith
# Works with object property assignment
math.add 1 2 into $obj.sum
RobinPath supports accessing object properties and array elements directly using dot notation and bracket notation:
Property Access:
json.parse '{"name": "John", "age": 30, "address": {"city": "NYC"}}'
$user = $
# Access properties using dot notation
log $user.name # Prints "John"
log $user.age # Prints 30
log $user.address.city # Prints "NYC" (nested property access)
# Use in expressions
if $user.age >= 18
log "Adult"
endif
# Use as function arguments
math.add $user.age 5 # Adds 30 + 5 = 35
Array Indexing:
$arr = range 10 15
log $arr[0] # Prints 10 (first element)
log $arr[2] # Prints 12 (third element)
log $arr[5] # Prints 15 (last element)
# Out of bounds access returns null
log $arr[10] # Prints null
# Use in expressions
if $arr[0] == 10
log "First is 10"
endif
Combined Access: You can combine property access and array indexing:
json.parse '{"items": [{"name": "item1", "price": 10}, {"name": "item2", "price": 20}]}'
$data = $
log $data.items[0].name # Prints "item1"
log $data.items[0].price # Prints 10
log $data.items[1].name # Prints "item2"
# Assign to variables
$firstItem = $data.items[0].name
log $firstItem # Prints "item1"
Error Handling:
- Accessing a property of
nullorundefinedthrows an error. - Accessing a property of a non-object throws an error.
- Array indexing on a non-array throws an error.
- Out-of-bounds array access returns
null.
Note: Assignment targets must be simple variable names or object/array paths:
$user.name = "Jane" # Supported
$arr[0] = 42 # Supported
$data.items[0].price = 99 # Deep assignment supported
RobinPath includes several built-in reserved methods:
log - Output values:
log "Hello, World!"
log $name $age
log "Result:" $(add 5 5)
obj - Create objects using JSON5 syntax or builder pattern:
# Create empty object
obj
$empty = $
# Create object with JSON5 syntax
obj '{name: "John", age: 30}'
$user = $
# Create object with key-value pairs (builder style)
obj name "John" age 30
array - Create arrays from arguments:
# Create empty array
array
$empty = $
# Create array with elements
array 1 2 3
$numbers = $
set - Assign a value:
set $var "value"
set $var as "value" # 'as' is optional
set $obj.prop "value" # path assignment
# Set with fallback (used if value is null/empty)
set $var $maybeNull "default value"
get - Get a value by path:
get $user "address.city"
assign - Assign a value to a variable (with optional fallback):
# Basic assignment
assign $myVar "hello"
# Assignment with fallback (3rd parameter used if 2nd is empty/null)
assign $result $maybeEmpty "default value"
empty - Clear/empty a variable:
$myVar = "some value"
empty $myVar
log $myVar # Prints null
fallback - Return variable value or fallback if empty/null:
$maybeEmpty = null
fallback $maybeEmpty "default value" # Returns "default value"
meta / getMeta - Manage metadata:
# Set metadata for variables, functions, or constants
meta $var description "Description"
meta myFunction author "Author"
meta $const version "1.0"
# Get metadata
getMeta $var description
getMeta myFunction author
clear - Clear the last return value ($):
math.add 10 20
clear
log $ # Prints null
forget - Hide a variable or function in the current scope:
$val = 10
do
forget $val
log $val # Prints null
enddo
getType - Get the type of a variable:
getType "hello" # "string"
getType 42 # "number"
getType true # "boolean"
getType null # "null"
getType [] # "array"
getType {} # "object"
has - Check if a variable or function exists:
has $var
has myFunction
has math.add
end - Stop script execution:
log "Start"
end
log "This is never reached"
do blocks create a new scope. Variables defined inside are local to the scope.
do
$local = 10
enddo
log $local # Prints null (if not defined globally)
Isolated Scopes with Parameters:
Scopes can be declared with parameters to create isolated execution contexts. Unlike regular do blocks, isolated scopes do not inherit variables from parent scopes unless they are explicitly passed as arguments.
$outer = 100
do $a $b
# $outer is NOT accessible here (returns null)
# $a and $b are initialized from arguments passed to the block
$inner = 20
enddo
Lines starting with # are comments:
# This is a comment
add 1 2 # Inline comment
Inline if:
if $age >= 18 then log "Adult"
Block if:
if $score >= 90
log "Grade: A"
elseif $score >= 80
log "Grade: B"
else
log "Grade: F"
endif
For loops:
# Iterate over a range
for $i from 1 to 5
log "Iteration:" $i
endfor
# Iterate with custom step and key (index)
for $a from 0 to 10 step 2 key $index
log "value: " + $a + ", index: " + $index
endfor
# Range parameters can be in any order
for $x by 5 to 100 from 0
log $x
endfor
# Iterate over an array
$numbers = [10, 11, 12]
for $num in $numbers key $i
log "Number " + $i + ": " + $num
endfor
Repeat loop:
The repeat loop runs a block N times. Inside the block:
$1is the current iteration index (0-based).$2is the result of the previous iteration (ornullfor the first).
repeat 5 with
log "Iteration:" $1 # 0, 1, 2, 3, 4
endwith
# Accumulate values
repeat 5 with
if $2 == null
return 0
endif
math.add $2 $1 # Adds current index to sum
endwith
# Result: 10 (0+1+2+3+4)
Break and Continue:
Use break to exit a loop early, and continue to skip to the next iteration.
for $i in range 1 10
if $i == 5
break
endif
endfor
Define custom functions with def (or define):
def greet $name
log "Hello" $name
enddef
# 'define' alias works too
define sum $a $b
math.add $a $b
enddef
Named Parameters:
You can optionally use as after parameters.
def greet $name $age as
# $name is alias for $1
# $age is alias for $2
log "Hello" $name "Age" $age
enddef
Call Syntax:
# CLI-style
greet "Alice" 25
# Parenthesized style (commas optional)
greet("Alice" 25)
greet(
"Alice"
25
)
Named Arguments:
def config $env
log "Env:" $env "Key:" $args.key
enddef
config("prod" key="123")
config(
"prod"
key="123"
)
Decorators: Use decorators to add metadata to functions and variables:
@desc "Calculates sum"
@param number $a "First number"
@param number $b "Second number"
@return number "The sum"
def add $a $b
math.add $a $b
enddef
Common decorators: @desc, @title, @param, @arg, @required, @return, @deprecated.
Define event handlers with on. Multiple handlers can be defined for the same event.
on "user_login"
log "User logged in:" $1
endon
# Trigger an event
trigger "user_login" "Alice"
Use together to run blocks in parallel:
together
do
# Task 1
wait 1000
log "Task 1 done"
enddo
do
# Task 2
log "Task 2 done"
enddo
endtogether
Use modules to access specialized functions:
use math
math.add 5 10
Available Modules:
math:add,subtract,multiply,divide,modulo,power,sqrt,abs,round,floor,ceil,min,max.string:length,substring,toUpperCase,toLowerCase,trim,replace,replaceAll,split,startsWith,endsWith,contains,indexOf,lastIndexOf,charAt,padStart,padEnd,repeat,concat.json:parse,stringify,isValid.object:keys,values,entries,merge,clone. (Global commands likekeysalso available).time:now,timestamp,format,addDays,diffDays.random:int,float,uuid,choice.array:create,length,get,slice,push,concat,join.dom:click(with callback support), etc.
Use $( ... ) for inline subexpressions. Subexpressions can be multi-line and contain multiple statements (returns result of the last one).
log "Result:" $(math.add 10 20)
$val = $(
math.add 5 5
math.multiply $ 2
)
# $val is 20
RobinPath provides built-in comparison functions useful for testing and assertions:
test.assertEqualtest.isEqualtest.isBiggertest.isSmallertest.isEqualOrBiggertest.isEqualOrSmallertest.assertTruetest.assertFalsetest.assertNulltest.assertNotNulltest.assertTypetest.assertContains
(Refer to "Creating Custom Modules" in the previous section of documentation for details on extending RobinPath with TypeScript).