mova

package module
v0.0.0-...-9636eb4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 10, 2025 License: Zlib Imports: 12 Imported by: 0

README

mova

mova is a small domain-specific language (DSL) for defining state machines with states, triggers, and actions. It is designed to be declarative, readable, and extensible, and serves as a simple way to express event-driven behavior in a static configuration file.

Overview

A mova file (.mova) describes one or more states, each with optional initial actions and triggers. A trigger listens for an event and can execute actions or move to another state.

The language supports:

  • constants via key = value;
  • states via state name { ... };
  • actions (function calls with arguments)
  • state transitions via move <state>
  • event-data via on EVENT(x=..., y=...) -> ACTIONS;

Syntax

1. Constants
press = 1;
release = 0;

Constants are variables and can later be used as action arguments or event-data. Supported types: integers, floats, strings, booleans.

2. States

Each state defines optional init actions and one or more triggers.

state init {
    set_led(led1=1, led2=0, led3=0, led4=0);
    on A(event=press) -> KEY_A, KEY_B;
    on B(event=press) -> move cursor;
};
  • state init { ... }; defines a named state.
  • The init section (before any on) runs when entering the state.
  • on EVENT(...) -> ...; defines a trigger and its reactions.
3. Triggers

Triggers react to incoming events. Each trigger may define one or more event-data fields that must match the trigger’s spec.

on ACCEL(x, y, z) -> MOUSE_MOVE(x, y, z);
  • Left side (x, y, z) = event-data received from an Emit() call.
  • Right side = one or more actions, separated by commas.
  • Each action can have arguments.
4. Actions

Actions refer to functions defined in the registry (e.g. KEY_A, MOUSE_MOVE, etc.). Arguments are evaluated as variables (constants or event-data).

MOUSE_MOVE(x=x, y=y, z=z);
set_led(led1=1, led2=0, led3=0, led4=0);
5. State Transitions

A transition moves execution to another state:

move cursor;

When executed, cursor becomes the active state. Its init actions will run automatically.

Full Example

# Wiimote mapping example

press   = 1;
release = 0;

state init {
    set_led(led1=1, led2=0, led3=0, led4=0);
    on A(event=press)   -> KEY_A, KEY_B;
    on B(event=press)   -> move cursor;
};

state cursor {
    set_led(led1=0, led2=1, led3=0, led4=0);
    on ACCEL(x, y, z)   -> MOUSE_MOVE(x, y, z);
    on A(event=press)   -> MOUSE_LEFT(event=press);
    on B(event=press)   -> move init;
};

Type Checking and Error Messages

mova enforces type consistency between constants, event-data, and actions.

Category Example message
Unknown trigger unspecified trigger "XYZ"
Unknown action unspecified action "SET_LED"
Missing event-data unspecified event-data "x" for trigger ACCEL
Type mismatch type mismatch for argument MOUSE_MOVE.x: expected ValueInt, got ValueString
Undefined variable undefined variable "foo"

Terminology is consistent across all errors:

  • unspecified → not declared in the spec
  • event-data → arguments passed to Emit
  • argument → parameters of an action call
  • variable → either a constant or event-data

Interpreter Architecture

The reference implementation consists of:

File Purpose
lexer.go Tokenizes source text
parser.go Builds an AST from tokens
statemachine.go Compiles the AST into an executable state machine
main.go Example usage with a Wiimote registry

File Extension

.mova (Alternative suggestions: .mach, .trig — depending on final project name.)

Design Goals

  • Minimal syntax, easy to read and parse
  • Consistent, precise error reporting
  • Embeddable in Go programs
  • Plan 9-inspired simplicity — no runtime magic, just parsing and execution

License

Zlib

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrDummyNotEvaluable = errors.New("Dummy Value not evaluable.")
View Source
var ErrEmptyMachine = errors.New("empty state machine")

Functions

func NewAction

func NewAction(r *Registry, name string, args []string, fn any)

func NewTrigger

func NewTrigger[T any](r *Registry, name string)

Types

type Action

type Action func(m *StateMachine, input map[string]Value) error

type ActionSpec

type ActionSpec struct {
	Inputs   []string      // expected input name -> type
	Function reflect.Value // executed with resolved inputs
}

type Arg

type Arg struct {
	Key   string
	Value Value
}

type Call

type Call struct {
	Name string
	Args map[string]Value
}

func (*Call) CheckType

func (c *Call) CheckType(ctx map[string]Value, m *CompiledMachine) error

func (*Call) Execute

func (c *Call) Execute(m *CompiledMachine) Action

type CompiledMachine

type CompiledMachine struct {
	// contains filtered or unexported fields
}

func BuildMachine

func BuildMachine(filename string, r io.Reader, reg *Registry, constants map[string]any) (*CompiledMachine, error)

func (*CompiledMachine) New

func (cm *CompiledMachine) New() (*StateMachine, error)

type CompiledState

type CompiledState struct {
	Init     []Action
	Triggers []CompiledTrigger
}

type CompiledTrigger

type CompiledTrigger struct {
	// contains filtered or unexported fields
}

func (CompiledTrigger) Test

func (trg CompiledTrigger) Test(name string, inputs reflect.Value) bool

type Condition

type Condition struct {
	TriggerName string
	Value       map[string]any
}

func (Condition) Test

func (cond Condition) Test(name string, inputs reflect.Value) bool

type ConstValue

type ConstValue struct {
	Value any
}

func (*ConstValue) EvalType

func (v *ConstValue) EvalType(ctx map[string]Value) (reflect.Type, error)

func (*ConstValue) EvalValue

func (v *ConstValue) EvalValue(ctx map[string]Value) (any, error)

type Entry

type Entry interface {
	EvalToplevel(*CompiledMachine) error
}

type File

type File struct {
	Entries []Entry
}

type MoveStmt

type MoveStmt struct {
	Dest string
}

func (*MoveStmt) CheckType

func (ms *MoveStmt) CheckType(_ map[string]Value, m *CompiledMachine) error

func (*MoveStmt) Execute

func (ms *MoveStmt) Execute(*CompiledMachine) Action

type ParseError

type ParseError struct {
	Filename             string
	Expected             []string
	Line, Offset, Length int
	Type, Value          string
}

func (*ParseError) Error

func (perr *ParseError) Error() string

type ReferenceValue

type ReferenceValue struct {
	Ref string
}

func (*ReferenceValue) EvalType

func (v *ReferenceValue) EvalType(ctx map[string]Value) (reflect.Type, error)

func (*ReferenceValue) EvalValue

func (v *ReferenceValue) EvalValue(ctx map[string]Value) (any, error)

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

type SetStmt

type SetStmt struct {
	Key   string
	Value Value
}

func (*SetStmt) EvalToplevel

func (ss *SetStmt) EvalToplevel(m *CompiledMachine) error

type State

type State struct {
	Name     string
	Init     []Statement
	Triggers []Trigger
}

func (*State) EvalToplevel

func (st *State) EvalToplevel(m *CompiledMachine) error

type StateMachine

type StateMachine struct {
	CompiledMachine
	// contains filtered or unexported fields
}

func (*StateMachine) Emit

func (m *StateMachine) Emit(name string, v any) error

type Statement

type Statement interface {
	CheckType(map[string]Value, *CompiledMachine) error
	Execute(*CompiledMachine) Action
}

type Trigger

type Trigger struct {
	Cond    []TriggerCond
	Actions []Statement
}

type TriggerCond

type TriggerCond struct {
	Name   string
	Params []Arg
}

type TypeDummyValue

type TypeDummyValue struct {
	// contains filtered or unexported fields
}

func (*TypeDummyValue) EvalType

func (v *TypeDummyValue) EvalType(ctx map[string]Value) (reflect.Type, error)

func (*TypeDummyValue) EvalValue

func (v *TypeDummyValue) EvalValue(ctx map[string]Value) (any, error)

type Value

type Value interface {
	EvalValue(ctx map[string]Value) (any, error)
	EvalType(ctx map[string]Value) (reflect.Type, error)
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL