import type { Action } from '@/types/scene' export interface ParseError { line: number message: string } export interface ParseResult { actions: Action[] errors: ParseError[] } /** * Parse a small line-based DSL into Action[]. Comments start with `//` * and each non-empty line is one command. Known commands: * * play | pause | rewind * jump (e.g. `jump 2.5`) * jump # (e.g. `jump #135`) * jump @ (e.g. `jump @intro`) */ export function parseScript(source: string): ParseResult { const actions: Action[] = [] const errors: ParseError[] = [] const lines = source.split(/\r?\n/) for (let i = 0; i < lines.length; i++) { const stripped = stripComment(lines[i]).trim() if (!stripped) continue const res = parseLine(stripped) if (res.error) errors.push({ line: i + 1, message: res.error }) else if (res.action) actions.push(res.action) } return { actions, errors } } function stripComment(line: string): string { const i = line.indexOf('//') return i < 0 ? line : line.slice(0, i) } function parseLine(line: string): { action?: Action; error?: string } { const tokens = line.split(/\s+/).filter(Boolean) if (!tokens.length) return {} const cmd = tokens[0].toLowerCase() switch (cmd) { case 'play': return ok({ type: 'play' }) case 'pause': return ok({ type: 'pause' }) case 'rewind': return ok({ type: 'rewindToLoop' }) case 'jump': return parseJump(tokens) default: return { error: `unknown command "${cmd}"` } } } function ok(action: Action): { action: Action } { return { action } } function parseJump(tokens: string[]): { action?: Action; error?: string } { if (tokens.length < 2) return { error: 'jump: missing argument' } const arg = tokens[1] if (arg.startsWith('#')) { const frame = Number(arg.slice(1)) if (!Number.isFinite(frame)) return { error: `jump: invalid frame "${arg}"` } return ok({ type: 'jumpToFrame', frame }) } if (arg.startsWith('@')) { const name = arg.slice(1) if (!name) return { error: 'jump: missing marker name after @' } return ok({ type: 'jumpToMarker', name }) } const time = Number(arg) if (!Number.isFinite(time)) return { error: `jump: invalid time "${arg}"` } return ok({ type: 'jumpTo', time }) }