Plan Syntax

Structure and syntax of OctoMY™ Plans

Plan Syntax

Complete reference for the structure and syntax of OctoMY™ Plans written in OPAL.

Did You Know?

OPAL syntax is intentionally similar to JavaScript/TypeScript - if you know JS, you'll feel right at home. The key differences are robot-specific: drive(), delay(), sensor access, and the init/loop/cleanup lifecycle blocks that structure robot behavior.


Plan file structure

A Plan file consists of these sections:

#!octomy-plan v1.0
# Metadata comments
# @name My Plan Name
# @version 1.0.0
# @author Your Name
# @permissions sensors.read, actuators.motors

// Imports (optional)
import "utils.opal"

// Constants
const MAX_SPEED = 100
const SAFE_DISTANCE = 30

// Global variables
var state = "idle"

// Custom functions
function helper() {
    // ...
}

// Main plan declaration
plan MyPlan {
    // Plan body
}

Shebang and version

Every Plan starts with a shebang declaring the OPAL version:

#!octomy-plan v1.0
Version Features
v1.0 Core OPAL language
v1.1 Added async/await
v1.2 Added pattern matching

Metadata comments

Metadata is declared using special comment syntax:

# @name Obstacle Avoider
# @version 2.1.0
# @author RobotBuilder42
# @description Autonomous obstacle avoidance using distance sensors
# @permissions sensors.distance, actuators.motors
# @requires firmware >= 1.5
# @tags navigation, autonomous, safety

Required metadata

Tag Description
@name Human-readable plan name
@permissions Required permissions (comma-separated)

Optional metadata

Tag Description
@version Semantic version (major.minor.patch)
@author Author name or identifier
@description Brief description
@requires System requirements
@tags Categorization tags
@license License identifier

Plan declaration

Plans are declared with the plan keyword:

plan PlanName {
    // Plan body
}

Plan name rules

  • Must start with a letter
  • Can contain letters, numbers, underscores
  • Convention: PascalCase for plan names
// Valid names
plan MyPlan { }
plan ObstacleAvoider_v2 { }
plan Plan123 { }

// Invalid names
plan 123Plan { }      // Cannot start with number
plan my-plan { }      // No hyphens allowed
plan plan { }         // Reserved word

Plan body sections

init block

Runs once when Plan activates:

plan Example {
    init {
        log("Plan starting")
        state = "ready"
        calibrate_sensors()
    }
}

loop block

Runs continuously while Plan is active:

plan Example {
    loop {
        check_sensors()
        update_motors()
        delay(50)  // Required delay
    }
}

Important: Loop blocks must include a delay() to prevent CPU overload.

cleanup block

Runs when Plan deactivates:

plan Example {
    cleanup {
        stop()
        log("Plan stopped")
        save_state()
    }
}

Complete structure

plan FullExample {
    init {
        // Initialization code
    }

    loop {
        // Main loop code
        delay(50)
    }

    cleanup {
        // Cleanup code
    }
}

Variables

Variable declaration

var name = value

Types

Type Example Description
Number 42, 3.14 Integer or floating point
String "hello" Text in double quotes
Boolean true, false Logical value
Array [1, 2, 3] Ordered collection
Object {x: 1, y: 2} Key-value pairs
null null No value
var count = 0
var name = "Robot"
var active = true
var readings = [10, 20, 30]
var config = {speed: 50, threshold: 30}
var empty = null

Constants

Constants cannot be reassigned:

const PI = 3.14159
const MAX_SPEED = 100

// Error: cannot reassign constant
MAX_SPEED = 200  // Throws error

Scope

Variables have block scope:

var global_var = 1

plan Example {
    var plan_var = 2

    init {
        var init_var = 3
        log(global_var)   // OK - global scope
        log(plan_var)     // OK - plan scope
        log(init_var)     // OK - local scope
    }

    loop {
        log(global_var)   // OK
        log(plan_var)     // OK
        log(init_var)     // Error - out of scope
        delay(100)
    }
}

Operators

Arithmetic

Operator Description Example
+ Addition 5 + 38
- Subtraction 5 - 32
* Multiplication 5 * 315
/ Division 6 / 32
% Modulo 7 % 31

Comparison

Operator Description Example
== Equal 5 == 5true
!= Not equal 5 != 3true
< Less than 3 < 5true
> Greater than 5 > 3true
<= Less or equal 5 <= 5true
>= Greater or equal 5 >= 3true

Logical

Operator Description Example
&& Logical AND true && falsefalse
|| Logical OR true || falsetrue
! Logical NOT !truefalse

Assignment

Operator Description Example
= Assign x = 5
+= Add and assign x += 3 (same as x = x + 3)
-= Subtract and assign x -= 3
*= Multiply and assign x *= 3
/= Divide and assign x /= 3

Control flow

Conditionals

if condition {
    // code
}

if condition {
    // code
} else {
    // code
}

if condition1 {
    // code
} else if condition2 {
    // code
} else {
    // code
}

Loops

For loop with range:

for i in 0..10 {
    log(i)  // 0, 1, 2, ... 9
}

For loop with collection:

var items = ["a", "b", "c"]
for item in items {
    log(item)
}

While loop:

while condition {
    // code
}

Infinite loop:

loop {
    // code
    delay(50)
}

Loop control

for i in 0..100 {
    if i == 50 {
        break  // Exit loop
    }
    if i % 2 == 0 {
        continue  // Skip to next iteration
    }
    log(i)
}

Functions

Function declaration

function name(parameters) {
    // code
    return value
}

Examples

// No parameters
function greet() {
    log("Hello!")
}

// With parameters
function add(a, b) {
    return a + b
}

// Default parameters
function drive_forward(speed = 50) {
    drive(speed, speed)
}

// Multiple return (via object)
function get_position() {
    return {x: position.x, y: position.y}
}

Calling functions

greet()
var sum = add(5, 3)
drive_forward()       // Uses default: 50
drive_forward(80)     // Override: 80
var pos = get_position()

Lambda expressions

Short function syntax:

// Single expression
var double = x -> x * 2

// Multiple parameters
var add = (a, b) -> a + b

// With block body
var process = x -> {
    var result = x * 2
    return result + 1
}

Usage with array functions

var numbers = [1, 2, 3, 4, 5]

var doubled = map(numbers, x -> x * 2)
// [2, 4, 6, 8, 10]

var evens = filter(numbers, x -> x % 2 == 0)
// [2, 4]

var sum = reduce(numbers, (acc, x) -> acc + x, 0)
// 15

Event handlers

Message handler

on_message("event_name") { data ->
    log("Received: " + data)
}

Multiple handlers

plan EventDemo {
    on_message("start") { _ ->
        log("Starting...")
    }

    on_message("stop") { _ ->
        log("Stopping...")
    }

    on_message("data") { payload ->
        process(payload)
    }

    loop {
        delay(100)
    }
}

Error handling

try/catch

try {
    // Code that might fail
    risky_operation()
} catch error {
    // Handle error
    log_error("Failed: " + error.message)
}

throw

function validate(value) {
    if value < 0 {
        throw "Value must be positive"
    }
    return value
}

Error object

catch error {
    error.code      // Error code (e.g., "E201")
    error.message   // Human-readable message
    error.line      // Line number
    error.stack     // Call stack
}

Comments

Single-line comments

// This is a comment
var x = 5  // Inline comment

Multi-line comments

/*
 * This is a
 * multi-line comment
 */

Documentation comments

/**
 * Calculates distance between two points.
 * @param x1 First x coordinate
 * @param y1 First y coordinate
 * @param x2 Second x coordinate
 * @param y2 Second y coordinate
 * @return Distance in units
 */
function distance(x1, y1, x2, y2) {
    var dx = x2 - x1
    var dy = y2 - y1
    return sqrt(dx*dx + dy*dy)
}

Property access

Dot notation

var dist = sensors.distance.front
var heading = sensors.imu.heading

Bracket notation

var sensor_name = "front"
var dist = sensors.distance[sensor_name]

Safe navigation

// Returns null if path doesn't exist
var value = data?.nested?.property

String interpolation

var name = "Robot"
var speed = 50

// Concatenation
log("Name: " + name + ", Speed: " + speed)

// Template strings (v1.1+)
log(`Name: ${name}, Speed: ${speed}`)

Importing

Import other OPAL files

import "utilities.opal"
import "sensors/distance.opal"

Import specific items

import { helper, constant } from "utils.opal"

Reserved words

These words cannot be used as identifiers:

if, else, for, while, loop, break, continue, return,
var, const, function, plan, true, false, null,
try, catch, throw, import, from, in, init, cleanup,
on_message, and, or, not

Best practices

Naming conventions

// Variables: camelCase
var myVariable = 1
var sensorReading = 50

// Constants: UPPER_SNAKE_CASE
const MAX_SPEED = 100
const SAFE_DISTANCE = 30

// Functions: camelCase
function calculateDistance() { }

// Plans: PascalCase
plan ObstacleAvoider { }

Code organization

#!octomy-plan v1.0
# Metadata first

// Constants
const THRESHOLD = 30

// Helper functions
function helper() { }

// Main plan
plan Main {
    init { }
    loop { delay(50) }
    cleanup { }
}

Error handling

// Always wrap sensor access
try {
    var dist = sensors.distance.front
} catch error {
    log_error("Sensor error: " + error.message)
    stop()
}

In this section
Topics
reference plans syntax OPAL
See also