script

package
v0.1.0-beta.8 Latest Latest
Warning

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

Go to latest
Published: Sep 21, 2026 License: GPL-3.0 Imports: 4 Imported by: 0

Documentation

Overview

Package script defines RO script types, opcodes, and scope namespace constants used by the script engine.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompileInto

func CompileInto(src []byte, set *CompiledScriptSet) error

CompileInto parses src and appends every compiled block into the accumulator set. Callers that need to compile multiple files into one set (e.g. a directory of .txt files) should use this rather than Compile, which allocates a fresh set.

func DefaultBuiltins

func DefaultBuiltins() map[string]BuiltinFunc

DefaultBuiltins returns a fresh map of the dialog-subset builtins, ready to hand to NewVM. Callers extend this map with phase-specific builtins (select, menu, getitem, sc_start, …) by copying it and inserting their entries.

Types

type AssignStmt

type AssignStmt struct {
	//nolint:revive // Required by task spec.
	Lhs Expr
	Op  string
	//nolint:revive // Required by task spec.
	Rhs Expr
	// contains filtered or unexported fields
}

AssignStmt is `lhs = rhs` or `lhs <op>= rhs`. Op is the source-text operator (`=`, `+=`, etc.) for diagnostic fidelity; the compiler selects the matching VM opcode.

func NewAssignStmt

func NewAssignStmt(lhs Expr, op string, rhs Expr, pos Position) *AssignStmt

NewAssignStmt builds an assignment statement (`lhs = rhs` or `lhs op= rhs`).

func (*AssignStmt) Pos

func (s *AssignStmt) Pos() Position

Pos returns the statement's source position.

type BinExpr

type BinExpr struct {
	Op string
	//nolint:revive // Required by task spec.
	Lhs Expr
	//nolint:revive // Required by task spec.
	Rhs Expr
	// contains filtered or unexported fields
}

BinExpr is a binary operation: `Lhs Op Rhs`. Op is the source-text operator (`+`, `-`, `==`, etc.).

func NewBinExpr

func NewBinExpr(op string, lhs, rhs Expr, pos Position) *BinExpr

NewBinExpr builds a binary operation expression.

func (*BinExpr) Pos

func (e *BinExpr) Pos() Position

Pos returns the expression's source position.

type BlockStmt

type BlockStmt struct {
	Body []Stmt
	// contains filtered or unexported fields
}

BlockStmt is `{ stmts... }` used as a statement. rAthena scripts do not commonly nest blocks as standalone statements, but the parser accepts them so braced bodies can appear anywhere a statement is expected (e.g. inside a case or as a macro-style wrapper).

func NewBlockStmt

func NewBlockStmt(body []Stmt, pos Position) *BlockStmt

NewBlockStmt creates a block statement.

func (*BlockStmt) Pos

func (s *BlockStmt) Pos() Position

Pos returns the statement's source position.

type BreakStmt

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

BreakStmt is `break;` — exit the innermost loop or switch.

func NewBreakStmt

func NewBreakStmt(pos Position) *BreakStmt

NewBreakStmt builds a `break;` statement.

func (*BreakStmt) Pos

func (s *BreakStmt) Pos() Position

Pos returns the statement's source position.

type BuiltinFunc

type BuiltinFunc func(vm *VM, args []Value) (Value, control)

BuiltinFunc is a script builtin invoked by OpFunc. It receives the VM (for variable access) and the already-popped, correctly-ordered argument slice, and returns a result value plus a flow control.

type CallExpr

type CallExpr struct {
	Name string
	Args []Expr
	// contains filtered or unexported fields
}

CallExpr is a function call expression. Distinguishes between builtin and user-defined functions only by name lookup at compile time.

func NewCallExpr

func NewCallExpr(name string, args []Expr, pos Position) *CallExpr

NewCallExpr builds a function call expression.

func (*CallExpr) Pos

func (e *CallExpr) Pos() Position

Pos returns the expression's source position.

type CallStmt

type CallStmt struct {
	Name string
	Args []Expr
	// contains filtered or unexported fields
}

CallStmt is a builtin invocation (`set`, `mes`, `warp`, ...) emitted as a statement. Args holds the parsed argument expressions; the Name is resolved to a builtin at compile time.

func NewCallStmt

func NewCallStmt(name string, args []Expr, pos Position) *CallStmt

NewCallStmt builds a builtin call statement.

func (*CallStmt) Pos

func (s *CallStmt) Pos() Position

Pos returns the statement's source position.

type CallSubStmt

type CallSubStmt struct {
	Label string
	Args  []Expr
	// contains filtered or unexported fields
}

CallSubStmt is `callsub Label, args...;`.

func NewCallSubStmt

func NewCallSubStmt(label string, args []Expr, pos Position) *CallSubStmt

NewCallSubStmt builds a `callsub Label, args...;` statement.

func (*CallSubStmt) Pos

func (s *CallSubStmt) Pos() Position

Pos returns the statement's source position.

type CompiledScript

type CompiledScript struct {
	Name         string
	Instructions []Instruction
	Labels       map[string]int
}

CompiledScript is the bytecode for a single NPC script or named function script. Labels maps label names to the instruction index of the OpLabel that introduces them; the compiler backpatches OpGoto/OpCallSub/CFunc targets through this map.

func NewCompiledScript

func NewCompiledScript(name string) *CompiledScript

NewCompiledScript returns an empty CompiledScript with an initialized Labels map. Use this rather than a struct literal to guarantee the map is non-nil so label inserts don't panic.

func (*CompiledScript) LookupLabel

func (c *CompiledScript) LookupLabel(name string) (int, bool)

LookupLabel returns the instruction index for a label and whether the label was found.

type CompiledScriptSet

type CompiledScriptSet struct {
	Scripts map[string]*CompiledScript
	Funcs   map[string]*CompiledScript
	Warps   []WarpDef
	Shops   []ShopDef
	// NPCs records the placement of every placed (non-floating) dialog NPC,
	// in compile order. The world seeder turns each into a spawned NPC
	// entity registered under its script name.
	NPCs    []NPCDef
	Spawns  []SpawnDef
	MapName string
}

CompiledScriptSet is the immutable set of all compiled scripts at a snapshot in time. The script engine swaps an atomic.Pointer[CompiledScriptSet] to hot-reload new versions without interrupting in-flight VMs (per design decision D-003).

func Compile

func Compile(src []byte) (*CompiledScriptSet, error)

Compile is the top-level entry: parse src, compile every block, and return a CompiledScriptSet keyed by NPC name with warps/shops extracted from headers. Floating `function script` blocks land in Funcs; placed/floating NPC blocks land in Scripts under the NPC name.

func NewCompiledScriptSet

func NewCompiledScriptSet() *CompiledScriptSet

NewCompiledScriptSet returns an empty set with all maps initialized.

type ContinueStmt

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

ContinueStmt is `continue;` — restart the innermost loop.

func NewContinueStmt

func NewContinueStmt(pos Position) *ContinueStmt

NewContinueStmt builds a `continue;` statement.

func (*ContinueStmt) Pos

func (s *ContinueStmt) Pos() Position

Pos returns the statement's source position.

type DoWhileStmt

type DoWhileStmt struct {
	Body []Stmt
	Cond Expr
	// contains filtered or unexported fields
}

DoWhileStmt is `do { ... } while (cond);`.

func NewDoWhileStmt

func NewDoWhileStmt(body []Stmt, cond Expr, pos Position) *DoWhileStmt

NewDoWhileStmt builds a `do { ... } while (cond);` statement.

func (*DoWhileStmt) Pos

func (s *DoWhileStmt) Pos() Position

Pos returns the statement's source position.

type Expr

type Expr interface {
	Node
	// contains filtered or unexported methods
}

Expr is an expression node. Expressions yield a Value when evaluated.

type File

type File struct {
	Body      []Stmt
	ShopItems []ShopItem
	// contains filtered or unexported fields
}

File represents a parsed script file: a tab-separated NPC header (possibly nil for floating `function script` definitions) followed by a list of body statements. For `shop` NPCs the body is empty and ShopItems carries the comma-separated item table instead.

func NewFile

func NewFile(header *NPCHeader, body []Stmt, shopItems ...ShopItem) *File

NewFile builds a File with the given header, body, and optional shop items. Header may be nil for floating function-script definitions. ShopItems are used by `shop` NPCs (which have no dialog body).

func Parse

func Parse(src []byte) ([]*File, error)

Parse tokenizes src with Lex and parses every top-level NPC block it contains, returning one *File per block. A block is a placed NPC (`map,x,y,dir script Name sprite,{ ... }`), a floating script (`- script Name -1,{ ... }`), or a function script (`function script Name { ... }`). Whitespace and comments are already gone by the time the parser sees the token stream.

func (*File) Header

func (f *File) Header() *NPCHeader

Header returns the file's NPC header (or nil for floating).

func (*File) Items

func (f *File) Items() []ShopItem

ShopItems returns the NPC's shop item table (empty for non-shop NPCs).

func (*File) Pos

func (f *File) Pos() Position

Pos returns the position of the first body statement, or the header if the body is empty.

type FloatLit

type FloatLit struct {
	Value float64
	// contains filtered or unexported fields
}

FloatLit is a float literal. Rare in rAthena scripts.

func NewFloatLit

func NewFloatLit(v float64, pos Position) *FloatLit

NewFloatLit builds a floating-point literal expression.

func (*FloatLit) Pos

func (e *FloatLit) Pos() Position

Pos returns the expression's source position.

type ForStmt

type ForStmt struct {
	Init []Stmt
	Cond Expr
	Post []Stmt
	Body []Stmt
	// contains filtered or unexported fields
}

ForStmt is `for (init; cond; post) { ... }`. Each clause is a slice because the for-init grammar allows comma-separated statements.

func NewForStmt

func NewForStmt(init []Stmt, cond Expr, post []Stmt, body []Stmt, pos Position) *ForStmt

NewForStmt builds a `for (init; cond; post) { ... }` statement.

func (*ForStmt) Pos

func (s *ForStmt) Pos() Position

Pos returns the statement's source position.

type FuncDecl

type FuncDecl struct {
	Name string
	Body []Stmt
	// contains filtered or unexported fields
}

FuncDecl is `function script F_Name { ... }` — a global function definition. Stored in CompiledScriptSet.Funcs.

func NewFuncDecl

func NewFuncDecl(name string, body []Stmt, pos Position) *FuncDecl

NewFuncDecl builds a `function script Name { ... }` declaration.

func (*FuncDecl) Pos

func (s *FuncDecl) Pos() Position

Pos returns the statement's source position.

type FuncRefStmt

type FuncRefStmt struct {
	Name string
	// contains filtered or unexported fields
}

FuncRefStmt is `function F_Name;` — a forward or backward reference to a function defined elsewhere. It has no body and is compiled as a no-op; it merely marks that the name is reachable.

func NewFuncRefStmt

func NewFuncRefStmt(name string, pos Position) *FuncRefStmt

NewFuncRefStmt builds a `function Name;` reference statement.

func (*FuncRefStmt) Pos

func (s *FuncRefStmt) Pos() Position

Pos returns the statement's source position.

type GotoStmt

type GotoStmt struct {
	Label string
	// contains filtered or unexported fields
}

GotoStmt is `goto Label;`.

func NewGotoStmt

func NewGotoStmt(label string, pos Position) *GotoStmt

NewGotoStmt builds a `goto Label;` statement.

func (*GotoStmt) Pos

func (s *GotoStmt) Pos() Position

Pos returns the statement's source position.

type Host

type Host interface {
	// Mes sends one dialog line (ZC_SCRIPT). Does not block.
	Mes(msg string)
	// Next sends the "click Next" prompt and blocks until the client
	// advances the dialog. Returns false if the dialog was closed or the
	// connection dropped, in which case the VM ends the script.
	Next() bool
	// Select sends the menu option list (ZC_MENU_LIST) and blocks until the
	// client chooses. options is the flat list (colon-separated inputs are
	// split by the builtin before calling). Returns the 1-based index the
	// client picked, or 255 (FF_CANCEL) for cancel, mirroring rAthena's
	// @menu / sd->npc_menu result of clif_parse_NpcSelectMenu.
	Select(options []string) int
	// Input sends the numeric-input dialog (ZC_OPEN_EDITDLG) and blocks until
	// the client replies with CZ_INPUT_EDITDLG. Returns the entered amount and
	// false if the dialog was closed or the connection dropped, in which case
	// the VM ends the script — mirroring Next()'s false return.
	Input() (amount int64, ok bool)
	// InputStr sends the string-input dialog (ZC_OPEN_EDITDLGSTR) and blocks
	// until the client replies with CZ_INPUT_EDITDLGSTR. Returns the entered
	// text and false if the dialog was closed or the connection dropped.
	InputStr() (text string, ok bool)
	// Close sends the close-dialog frame (ZC_CLOSE_DIALOG).
	Close()
	// Warp moves the player to the named map tile.
	Warp(mapName string, x, y int)
	// PercentHeal restores HP/SP by the given percentages (0–100).
	PercentHeal(hpPct, spPct int)
	// GetItem grants amount units of nameID. Returns false when the bag is
	// full / the item cannot be added.
	GetItem(nameID uint32, amount int) bool
	// DelItem removes amount units of nameID. Returns false when the player
	// does not hold at least `amount` of it.
	DelItem(nameID uint32, amount int) bool
	// CountItem returns the player's count of nameID.
	CountItem(nameID uint32) int
	// Equip equips the item at the inventory index to the given slot bitmask.
	// Returns false when the index is invalid.
	Equip(index int, slot uint32) bool
	// Unequip clears the equip bitmask of the item at the inventory index.
	// Returns false when the index is invalid or the item is not equipped.
	Unequip(index int) bool
	// GetQuestVar returns the persistent NPC-variable value (npcName, varName)
	// for the dialog's player. An unset variable reads as 0 — matches
	// rAthena's "unset integer reads as 0". The persistence port is implemented
	// by the content bounded context's QuestService; nil disables the
	// getvariableofnpc builtin (returns 0).
	GetQuestVar(npcName, varName string) int64
	// SetQuestVar stores the persistent NPC-variable value (npcName, varName)
	// for the dialog's player. A write error is returned to the caller; the VM
	// stops with ctrlEnd on a hard write failure (mismatching the rAthena
	// behaviour where a quest-table failure logs but does not abort the
	// script). nil disables the persistence side of the builtin.
	SetQuestVar(npcName, varName string, value int64) error
}

Host is the seam between the VM and the game server. The dialog builtins produce output here and may block the VM goroutine: Next() suspends the script until the client sends CZ_REQ_NEXT_SCRIPT, then returns. This is the goroutine-per-dialog model — the script's sequential state lives on the goroutine stack, so no manual IP serialization is needed across the wait. Effects (warp/heal) flow back through the Host to the world module.

type IdentExpr

type IdentExpr struct {
	Name string
	// contains filtered or unexported fields
}

IdentExpr is a variable reference, possibly prefixed (`.@`, `@`, `#`, `$`, `'`) and possibly suffixed with `$` for string variables. Name preserves the source text exactly.

func NewIdentExpr

func NewIdentExpr(name string, pos Position) *IdentExpr

NewIdentExpr builds an identifier/variable reference expression.

func (*IdentExpr) Pos

func (e *IdentExpr) Pos() Position

Pos returns the expression's source position.

type IfStmt

type IfStmt struct {
	Cond Expr
	Then []Stmt
	Else []Stmt
	// contains filtered or unexported fields
}

IfStmt is `if (cond) { ... } else { ... }`. Else may be nil.

func NewIfStmt

func NewIfStmt(cond Expr, then, els []Stmt, pos Position) *IfStmt

NewIfStmt builds an `if (cond) { ... } else { ... }` statement.

func (*IfStmt) Pos

func (s *IfStmt) Pos() Position

Pos returns the statement's source position.

type IndexExpr

type IndexExpr struct {
	Target Expr
	Index  Expr
	// contains filtered or unexported fields
}

IndexExpr is `target[index]` — array element access. High 32 bits of the variable id encode the index (per rAthena reference_uid).

func NewIndexExpr

func NewIndexExpr(target, index Expr, pos Position) *IndexExpr

NewIndexExpr builds an array index expression (`target[index]`).

func (*IndexExpr) Pos

func (e *IndexExpr) Pos() Position

Pos returns the expression's source position.

type InputStmt

type InputStmt struct {
	Dst Expr
	// contains filtered or unexported fields
}

InputStmt is `input @var;` — read an integer from the player into a variable.

func NewInputStmt

func NewInputStmt(dst Expr, pos Position) *InputStmt

NewInputStmt builds an `input var;` statement.

func (*InputStmt) Pos

func (s *InputStmt) Pos() Position

Pos returns the statement's source position.

type Instruction

type Instruction struct {
	Op      Opcode
	Operand int32
	Str     string
	Pos     Position
}

Instruction is a single VM instruction. The Operand and Str fields carry the opcode-specific payload:

  • Operand is the int payload for OpInt (literal), OpLine (line number), and numeric label indices.
  • Str is the string payload for OpStr (string literal), OpVar (variable name), OpName (label/variable name), OpFunc (function name), OpLabel/OpGoto/OpCallSub (label name).

Both fields are present on every Instruction for simplicity; OpXXX constants that do not use a field leave it zero-valued.

Pos is the source location the instruction was compiled from. The VM uses it to format stack traces; the parser uses it to attach errors during compilation.

func (Instruction) String

func (i Instruction) String() string

String returns a disassembly-style line for debug printing.

type IntLit

type IntLit struct {
	Value int64
	// contains filtered or unexported fields
}

IntLit is an integer literal. Value is the parsed int64.

func NewIntLit

func NewIntLit(v int64, pos Position) *IntLit

NewIntLit builds an integer literal expression.

func (*IntLit) Pos

func (e *IntLit) Pos() Position

Pos returns the expression's source position.

type LabelDecl

type LabelDecl struct {
	Name string
	// contains filtered or unexported fields
}

LabelDecl is `L_Name:` — a label declaration. Standalone (not part of a `case`) and recognized at parse time.

func NewLabelDecl

func NewLabelDecl(name string, pos Position) *LabelDecl

NewLabelDecl builds a label declaration (`L_Name:`).

func (*LabelDecl) Pos

func (s *LabelDecl) Pos() Position

Pos returns the statement's source position.

type LexError

type LexError struct {
	Pos Position
	Msg string
}

LexError is returned by Lex when the source contains a tokenization error (unterminated string, unexpected byte, etc.). It carries the source position of the failure so errors can be surfaced with line + column context.

func (*LexError) Error

func (e *LexError) Error() string

Error implements the error interface.

type MenuOption struct {
	Prompt Expr
	Label  string
	// contains filtered or unexported fields
}

MenuOption is one entry of a menu/select/prompt. Label is the jump target; the parser accepts either a bare identifier or a `callsub L` form via CallExpr.Name.

func NewMenuOption

func NewMenuOption(prompt Expr, label string, pos Position) MenuOption

NewMenuOption builds one prompt/label pair for menu/select.

func (m *MenuOption) Pos() Position

Pos returns the option's source position. Implements Node.

type MenuStmt struct {
	Options []MenuOption
	// contains filtered or unexported fields
}

MenuStmt is `menu("optA",L_a,"optB",L_b,...);` — pair-encoded options. Each option pairs a string prompt with a label or callsub target.

func NewMenuStmt

func NewMenuStmt(opts []MenuOption, pos Position) *MenuStmt

NewMenuStmt builds a `menu(...);` statement.

func (s *MenuStmt) Pos() Position

Pos returns the statement's source position.

type NPCDef

type NPCDef struct {
	Name    string
	MapName string
	X       int
	Y       int
	Facing  int
	Sprite  int
}

NPCDef is a placed dialog NPC: where it stands and the sprite the client renders. The script body lives in Scripts[Name].

type NPCHeader

type NPCHeader struct {
	MapName    string
	X          int
	Y          int
	Facing     int
	Name       string
	SpriteName string
	SpriteID   int
	TriggerX   int
	TriggerY   int
	Type       string
	// contains filtered or unexported fields
}

NPCHeader is the tab-separated NPC definition header. It captures every column of rAthena's npc_parsename layout (npc.cpp:3668).

func NewNPCHeader

func NewNPCHeader(mapName string, x, y, facing int, name, spriteName string,
	spriteID, triggerX, triggerY int, typ string, pos Position,
) *NPCHeader

NewNPCHeader builds an NPCHeader at the given source position.

func (*NPCHeader) Pos

func (h *NPCHeader) Pos() Position

Pos returns the header's source position.

func (*NPCHeader) String

func (h *NPCHeader) String() string

String returns a compact representation used in error messages and disassembly.

type Node

type Node interface {
	Pos() Position
	// contains filtered or unexported methods
}

Node is the root interface for all AST nodes. Every AST node knows its source position so that error messages and runtime diagnostics can point to the offending construct.

type Opcode

type Opcode uint8

Opcode is a VM operation code. It mirrors rAthena's c_op enum (rathena/src/map/script.hpp:222-265) but uses snake_case Go names and a compact 46-entry layout suitable for an Opcode uint8.

The mapping is intentionally pragmatic, not byte-for-byte: rAthena distinguishes e.g. C_FUNC (builtin marker) from C_USERFUNC (user function marker); here both are subsumed under OpFunc with a name lookup deciding the kind at compile time. C_POS, C_NAME, C_USERFUNC_POS collapse to OpLabel with a name operand.

Wire-level bytecode, when it is emitted by the compiler, will use a uint8 discriminant followed by the per-op operand layout described on each constant below.

const (
	// OpEOF marks the end of the bytecode stream. Halts the VM.
	OpEOF Opcode = iota

	// OpExpr is a unary expression evaluation marker. Operand = none.
	OpExpr

	// OpExpr2 is a binary expression evaluation marker. Operand = none.
	OpExpr2

	// OpLAnd is logical AND short-circuit: pops 2, pushes bool. Operand = none.
	OpLAnd

	// OpLOr is logical OR short-circuit: pops 2, pushes bool. Operand = none.
	OpLOr

	// OpLEq is `==` equality. Pops 2, pushes bool. Operand = none.
	OpLEq

	// OpLNe is `!=` inequality. Pops 2, pushes bool. Operand = none.
	OpLNe

	// OpLGT is `>` greater-than. Operand = none.
	OpLGT

	// OpLLT is `<` less-than. Operand = none.
	OpLLT

	// OpLGE is `>=` greater-or-equal. Operand = none.
	OpLGE

	// OpLLE is `<=` less-or-equal. Operand = none.
	OpLLE

	// OpAdd is `+` arithmetic. Operand = none.
	OpAdd

	// OpSub is `-` arithmetic. Operand = none.
	OpSub

	// OpMul is `*` arithmetic. Operand = none.
	OpMul

	// OpDiv is `/` arithmetic. Operand = none.
	OpDiv

	// OpMod is `%` remainder. Operand = none.
	OpMod

	// OpNeg is unary `-`. Operand = none.
	OpNeg

	// OpNot is unary `!`. Operand = none.
	OpNot

	// OpAnd is bitwise `&`. Operand = none.
	OpAnd

	// OpOr is bitwise `|`. Operand = none.
	OpOr

	// OpXor is bitwise `^`. Operand = none.
	OpXor

	// OpBNot is unary bitwise `~`. Operand = none.
	OpBNot

	// OpShiftL is `<<`. Operand = none.
	OpShiftL

	// OpShiftR is `>>`. Operand = none.
	OpShiftR

	// OpAssign is `=`. Operand = none.
	OpAssign

	// OpAssignAdd is `+=`. Operand = none.
	OpAssignAdd

	// OpAssignSub is `-=`. Operand = none.
	OpAssignSub

	// OpAssignMul is `*=`. Operand = none.
	OpAssignMul

	// OpAssignDiv is `/=`. Operand = none.
	OpAssignDiv

	// OpAssignMod is `%=`. Operand = none.
	OpAssignMod

	// OpAssignShiftL is `<<=`. Operand = none.
	OpAssignShiftL

	// OpAssignShiftR is `>>=`. Operand = none.
	OpAssignShiftR

	// OpLabel marks a label definition / target. Operand = label name
	// (look up via CompiledScript.Labels).
	OpLabel

	// OpGoto is unconditional jump to label. Operand = label name.
	OpGoto

	// OpCallSub is a same-script label call (push return info + jump).
	// Operand = label name.
	OpCallSub

	// OpReturn returns from the current callsub/callfunc scope.
	// Operand = none.
	OpReturn

	// OpBinary marks a binary op boundary for argument-list emission
	// (`C_ARG` analogue in rAthena). Operand = none.
	//
	// Phase R0 (S1) also reuses OpBinary as the array-READ operator:
	// when the compiler emits an IndexExpr over an IdentExpr target, it
	// pushes the array name as a string and the index as an int, then
	// OpBinary pops both and resolves to the stored value via
	// ScopeStore.GetArray. The Str field of the instruction itself is
	// not used by this path.
	OpBinary

	// OpPush pushes a constant value onto the stack. The constant's
	// discriminant is implicit in the Instruction's Str/Operand fields.
	OpPush

	// OpVar is a variable reference (resolve by Str to a scope-backed
	// storage handle). Operand = Str field = variable name.
	OpVar

	// OpFunc invokes a built-in function. Operand = Str field = function
	// name (resolved via the BuiltinRegistry at compile time).
	OpFunc

	// OpName references a symbol by name (label, variable, function).
	// Operand = Str field = name. Used for argument-list name binding.
	OpName

	// OpEnd ends the current script activation. Operand = none.
	OpEnd

	// OpClose closes the player dialog. Operand = none.
	OpClose

	// OpInt is an int64 literal. Operand int32 field = value.
	OpInt

	// OpStr is a string literal. Operand Str field = decoded string.
	OpStr

	// OpLine is a line-number marker for stack-trace diagnostics.
	// Operand int32 field = 1-indexed source line.
	OpLine

	// OpIndexGet is the array-READ operator. Reserved for parity with
	// rAthena's C_GETINDEX / OpIndexGet split; the current compiler
	// emits OpBinary for reads and the VM handles both opcodes the
	// same way.
	//
	// Stack contract: pops idx (top) then name (string), pushes the
	// stored array element (zero Value when the array or index is
	// absent).
	OpIndexGet

	// OpIndexSet is the array-WRITE operator. Operand Str field = name.
	//
	// Stack contract: pops name (top), idx (second), value (third),
	// then stores value into ScopeStore.SetArray(name, idx).
	OpIndexSet

	// OpJumpIfFalse pops a value; if it is falsy (IsZero) the VM jumps to
	// the instruction index named by the Str field (resolved through
	// CompiledScript.Labels). Otherwise execution falls through. Used to
	// compile if/while/for conditions and `&&` short-circuit. The popped
	// value is consumed either way.
	OpJumpIfFalse

	// OpJumpIfTrue is the dual of OpJumpIfFalse: pops a value and jumps to
	// the Str-named label only when the value is truthy. Used to compile
	// `||` short-circuit.
	OpJumpIfTrue

	// OpPop discards the top of the stack. A builtin call in statement
	// position (e.g. `mes "x";`) leaves its return value on the stack via
	// OpFunc; the compiler emits OpPop so the void result does not leak.
	// Expression-position calls (e.g. `select(...)`) keep the value for the
	// surrounding expression and emit no OpPop.
	OpPop
)

func (Opcode) String

func (o Opcode) String() string

String returns a mnemonic for debug disassembly.

type ParenExpr

type ParenExpr struct {
	Inner Expr
	// contains filtered or unexported fields
}

ParenExpr is `(expr)` — preserved for source-fidelity in error messages but usually collapsed by the parser.

func NewParenExpr

func NewParenExpr(inner Expr, pos Position) *ParenExpr

NewParenExpr builds a parenthesized expression (`(expr)`).

func (*ParenExpr) Pos

func (e *ParenExpr) Pos() Position

Pos returns the expression's source position.

type ParseError

type ParseError struct {
	Pos Position
	Msg string
}

ParseError carries the source position of a syntax failure. Its format mirrors LexError so callers render parse and lex errors uniformly.

func (*ParseError) Error

func (e *ParseError) Error() string

type Position

type Position struct {
	Line   int
	Column int
}

Position represents a source code position (line + column).

Line is 1-indexed; Column is 1-indexed (number of characters since the most recent line start). Column counts bytes, not runes — rAthena script sources are virtually always ASCII/EUC-KR which is single-byte.

func (Position) String

func (p Position) String() string

String formats the position as "line:col" for error messages.

type ReturnStmt

type ReturnStmt struct {
	Value Expr
	// contains filtered or unexported fields
}

ReturnStmt is `return [value];`.

func NewReturnStmt

func NewReturnStmt(value Expr, pos Position) *ReturnStmt

NewReturnStmt builds a `return [value];` statement.

func (*ReturnStmt) Pos

func (s *ReturnStmt) Pos() Position

Pos returns the statement's source position.

type SelectStmt

type SelectStmt struct {
	Options []MenuOption
	// contains filtered or unexported fields
}

SelectStmt is `select("...");` — like menu but the index is returned rather than jumping to a label.

func NewSelectStmt

func NewSelectStmt(opts []MenuOption, pos Position) *SelectStmt

NewSelectStmt builds a `select(...);` statement.

func (*SelectStmt) Pos

func (s *SelectStmt) Pos() Position

Pos returns the statement's source position.

type ShopDef

type ShopDef struct {
	Name    string
	MapName string
	X       int
	Y       int
	Facing  int
	Sprite  int
	Items   []ShopItem
}

ShopDef defines a shop NPC: the dialog routes into a merchant UI listing Items.

type ShopItem

type ShopItem struct {
	ItemID int32
	Price  int32
}

ShopItem is a single inventory slot offered by a ShopDef.

type SpawnDef

type SpawnDef struct {
	MapName string
	X       int
	Y       int
	XSize   int
	YSize   int
	Class   int32
	Name    string
	Amount  int
	Delay1  int
	Delay2  int
}

SpawnDef is one monster-spawn line (`map,x,y[,xs,ys] monster Name class,amount[,d1,d2]`). Amount mobs spawn inside the xs×ys cell box centered on (X,Y); d1/d2 bound the respawn delay window in seconds (rAthena: respawn after d1 + rand(d2-d1)).

func ParseSpawnLines

func ParseSpawnLines(src []byte) []SpawnDef

ParseSpawnLines extracts monster-spawn definitions from rAthena mob-spawn script text (`npc/pre-re/mobs/**.txt`). These files are not NPC scripts: each data line is

map,x,y[,xs,ys]<TAB>monster|boss_monster|mini<TAB>Name<TAB>class,amount[,d1,d2]

and the full NPC grammar rejects them, so this scanner runs as a fallback for files that fail Compile. Comment (`//`) and blank lines are skipped; lines that do not match are skipped rather than erroring — the corpus is large and heterogeneous, and a single odd line must not drop a file's spawns (mirrors the tolerant-loader doctrine in ADR-0003).

type Stmt

type Stmt interface {
	Node
	// contains filtered or unexported methods
}

Stmt is a statement node. Statements never appear in expression position; they have side effects.

type StrLit

type StrLit struct {
	Value string
	// contains filtered or unexported fields
}

StrLit is a string literal. Value is the decoded string with escape sequences resolved.

func NewStrLit

func NewStrLit(v string, pos Position) *StrLit

NewStrLit builds a string literal expression.

func (*StrLit) Pos

func (e *StrLit) Pos() Position

Pos returns the expression's source position.

type SwitchCase

type SwitchCase struct {
	Values []Expr
	Body   []Stmt
	// contains filtered or unexported fields
}

SwitchCase is one arm of a switch. Values may be empty for the default case.

func NewSwitchCase

func NewSwitchCase(values []Expr, body []Stmt, pos Position) SwitchCase

NewSwitchCase builds one case/default arm of a switch.

func (*SwitchCase) Pos

func (sc *SwitchCase) Pos() Position

Pos returns the case's source position. Implements Node.

type SwitchStmt

type SwitchStmt struct {
	Value Expr
	Cases []SwitchCase
	// contains filtered or unexported fields
}

SwitchStmt is `switch (val) { case X: ...; default: ...; }`. The parser lowers case labels to internal labels per rAthena.

func NewSwitchStmt

func NewSwitchStmt(val Expr, cases []SwitchCase, pos Position) *SwitchStmt

NewSwitchStmt builds a `switch (val) { ... }` statement.

func (*SwitchStmt) Pos

func (s *SwitchStmt) Pos() Position

Pos returns the statement's source position.

type TernaryExpr

type TernaryExpr struct {
	Cond Expr
	Then Expr
	Else Expr
	// contains filtered or unexported fields
}

TernaryExpr is `Cond ? Then : Else`. Lowered to OpExpr2 / OpLOr branch at compile time, but kept as a first-class AST node so error messages name it directly.

func NewTernaryExpr

func NewTernaryExpr(cond, then, els Expr, pos Position) *TernaryExpr

NewTernaryExpr builds a conditional expression (`cond ? then : else`).

func (*TernaryExpr) Pos

func (e *TernaryExpr) Pos() Position

Pos returns the expression's source position.

type Token

type Token struct {
	Kind  TokenKind
	Value string
	Pos   Position
}

Token is a single lexical unit produced by the scanner.

Pos is the source location of the first character of Value. For TokenEOF the position is the line/column immediately after the last consumed byte.

func Lex

func Lex(src []byte) ([]Token, error)

Lex tokenizes a rAthena script source buffer and returns the resulting token stream, terminated by TokenEOF. It returns an error only for unrecoverable lexing errors (unterminated string literal, unterminated block comment, or an unknown byte the scanner cannot classify). Whitespace and comments are silently skipped.

The lexer is a pure function: it does not retain references to the input buffer after returning. Tests can Lex a buffer, capture the tokens, and discard the buffer.

func (Token) String

func (t Token) String() string

String returns a debug-friendly representation like "IDENT(mes) @ 1:1". Useful for test failure messages and parser error contexts.

type TokenKind

type TokenKind int

TokenKind classifies a lexical token.

The set covers what a hand-written recursive-descent parser for the rAthena dialect needs to see. Comments and (most) whitespace are silently dropped by the lexer; newlines are tracked implicitly via the Position carried on each Token rather than being emitted as separate tokens, except inside switch/case dispatch where they are significant only as statement separators (`;`).

const (
	// TokenEOF is the sentinel emitted after the last byte of input.
	TokenEOF TokenKind = iota
	// TokenIdent is an identifier: variable, function, label name,
	// builtin name. The first character may be a scope prefix
	// (`.`, `@`, `#`, `$`, `'`) and the last character may be `$`
	// to mark a string-typed variable. Examples: `mes`, `.@var`,
	// `callfunc`, `L_Open`, `#account$`.
	TokenIdent
	// TokenInt is a decimal or hexadecimal integer literal.
	// Unary minus is NOT part of the literal — it is emitted as a
	// separate TokenOperator applied as a prefix unary.
	TokenInt
	// TokenFloat is a floating-point literal. Rare in rAthena scripts
	// but defined for forward compatibility with the rAthena parser.
	TokenFloat
	// TokenString is a `"..."` string literal. The Value field holds
	// the *decoded* string with escape sequences resolved; the raw
	// text is not preserved.
	TokenString
	// TokenKeyword is a reserved word: if, else, while, for, do,
	// switch, case, default, break, continue, function, return, goto,
	// callsub, callfunc, end, close, close2, next, mes, menu, select,
	// input, set, setarray, etc.
	TokenKeyword
	// TokenOperator is a non-assignment operator: +, -, *, /, %,
	// ==, !=, <, >, <=, >=, &&, ||, !, &, |, ^, ~, <<, >>, ++, --.
	TokenOperator
	// TokenAssign is an assignment operator: =, +=, -=, *=, /=, %=,
	// &=, |=, ^=, <<=, >>=.
	TokenAssign
	// TokenDelim is a delimiter: ; , : ( ) { } [ ] ?.
	TokenDelim
	// TokenComment is a `// line` or `/* block */` comment. Comments
	// are typically suppressed by the scanner but the kind exists
	// for diagnostics/debugging output.
	TokenComment
	// TokenNewline is emitted when the scanner observes a `\n` that
	// is significant for downstream parsing (e.g. to count lines). By
	// default the lexer skips newlines silently and the Position on
	// subsequent tokens carries the line number.
	TokenNewline
)

func (TokenKind) String

func (k TokenKind) String() string

String returns a human-readable name for the token kind.

type UnaryExpr

type UnaryExpr struct {
	Op      string
	Operand Expr
	// contains filtered or unexported fields
}

UnaryExpr is a prefix unary operation: `Op Operand`. Op is one of `-`, `!`, `~`, `++`, `--`.

func NewUnaryExpr

func NewUnaryExpr(op string, operand Expr, pos Position) *UnaryExpr

NewUnaryExpr builds a prefix or postfix unary expression.

func (*UnaryExpr) Pos

func (e *UnaryExpr) Pos() Position

Pos returns the expression's source position.

type VM

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

VM is a stack machine executing one CompiledScript. A fresh VM is created per dialog invocation; its goroutine owns its stack, IP, call stack, and temporary variables. The variable scope (vars) is the caller's — seeded with char vars before Run and read back after to persist changes — so the VM itself stays stateless across invocations.

func NewVM

func NewVM(cs *CompiledScript, host Host, builtins map[string]BuiltinFunc, vars map[string]Value) *VM

NewVM constructs a VM for cs. host is the I/O seam, builtins resolves OpFunc names (DefaultBuiltins covers the dialog subset), and vars is the initial variable scope (may be nil). After Run, mutated vars remain in the same map for the caller to persist.

func (*VM) GetVar

func (vm *VM) GetVar(name string) Value

GetVar returns a variable's value. An unset variable yields the zero Value (nil), which IsZero reports as false and asInt reads as 0 — matching rAthena's "unset integer reads as 0".

func (*VM) Run

func (vm *VM) Run() error

Run executes bytecode until OpEOF, a ctrlEnd builtin (end/close), or an error. Jumps set ip and continue the loop; every other op advances ip by one at the loop tail.

func (*VM) SetVar

func (vm *VM) SetVar(name string, v Value)

SetVar stores a variable, initializing the scope on first write.

func (*VM) Vars

func (vm *VM) Vars() map[string]Value

Vars returns the live variable map (mutated in place by SetVar/assignment). Callers read it after Run to persist char-var changes.

type Value

type Value struct {
	Kind ValueKind
	Int  int64
	Str  string
	Flt  float64
}

Value is a runtime script value. Only the field matching Kind is meaningful; the others stay zero. Value is immutable in practice — assignment replaces the whole cell, not a field of it.

func FloatVal

func FloatVal(f float64) Value

FloatVal returns a float Value.

func IntVal

func IntVal(n int64) Value

IntVal returns an integer Value.

func NilVal

func NilVal() Value

NilVal is the absent/zero value, returned for unset variables and void calls.

func StrVal

func StrVal(s string) Value

StrVal returns a string Value.

func (Value) IsZero

func (v Value) IsZero() bool

IsZero reports whether v is the falsy value for a conditional. rAthena treats 0, the empty string, and nil as false; any nonzero int, non-empty string, or any float (including 0.0, matching rAthena's float branch which is truthy unless it is the integer-zero representation) as true. Scripts overwhelmingly branch on integer results, so the int branch is the hot path.

func (Value) String

func (v Value) String() string

String returns the script-visible textual form. Integers render in decimal; strings render bare; nil renders as the empty string (rAthena prints an unset variable as "" when concatenated). Used by mes/concat and for debug.

type ValueKind

type ValueKind uint8

ValueKind discriminates the dynamic type of a script Value. rAthena's script language is loosely typed: a variable holds either an integer or a string (the `$` suffix on a name marks the string cell). Float is rare but defined for forward compatibility. The VM never auto-coerces across kinds except where a builtin explicitly does (e.g. string concat).

const (
	// KindNil is the zero value: an unset variable or an absent result.
	KindNil ValueKind = iota
	// KindInt is a 64-bit integer — the overwhelmingly common case.
	KindInt
	// KindStr is a UTF-8 string (variable cells suffixed `$`).
	KindStr
	// KindFloat is a 64-bit float; reserved, few builtins produce it.
	KindFloat
)

type WarpDef

type WarpDef struct {
	MapName  string
	X        int
	Y        int
	TriggerX int
	TriggerY int
	DestMap  string
	DestX    int
	DestY    int
}

WarpDef defines a warp portal: clicking the source tile teleports the player to DestMap/DestX/DestY.

func (WarpDef) Key

func (w WarpDef) Key() WarpKey

Key returns the (map, x, y) tuple identifying the source tile.

type WarpKey

type WarpKey struct {
	MapName  string
	TriggerX int
	TriggerY int
}

WarpKey identifies a warp portal by its source tile.

type WhileStmt

type WhileStmt struct {
	Cond Expr
	Body []Stmt
	// contains filtered or unexported fields
}

WhileStmt is `while (cond) { ... }`.

func NewWhileStmt

func NewWhileStmt(cond Expr, body []Stmt, pos Position) *WhileStmt

NewWhileStmt builds a `while (cond) { ... }` statement.

func (*WhileStmt) Pos

func (s *WhileStmt) Pos() Position

Pos returns the statement's source position.

Jump to

Keyboard shortcuts

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