flash-parser v0.0.1 flash-parser
A lossless CST + typed AST parser for the flash-theater DSL (.thr/.flsh)
— the counterpart to kopytko-brightscript-parser for BrightScript.
Used by flash-theater-compiler as its only
parsing layer.
Install
npm install flash-parserRequires Node.js ≥ 24.
Quick start
Parse a .thr file into a lossless CST + typed AST.
import { parseThr, findAll, SyntaxKind, FieldDeclaration } from 'flash-parser';
const source = `
<script>
field count: integer = 0
derived doubled: integer = count * 2
</script>
<component>
<Label id="label" text="{doubled}" />
</component>
`;
const { root, diagnostics } = parseThr(source);
diagnostics; // [] — no parse errors
// Lossless CST: printing every token's full text (with trivia) reproduces
// the exact original source, byte for byte.
root.getText() === source; // true
// findAll + one of the typed AST classes from 'ast.js' — walk() itself
// is a plain (node: SyntaxNode) => void callback over every CST node,
// not a per-kind visitor object; findAll is how you collect just one kind.
const fields = findAll(root, SyntaxKind.FieldDeclaration, (n) => new FieldDeclaration(n));
for (const f of fields) console.log(f.name, f.type, f.defaultLiteral);
// "count integer 0"Diagnostics
parseThr/parseFlsh never
throw on malformed source — a syntax problem always comes back as an entry in
diagnostics instead (this is also what
flash-theater-compiler's own
CompileError wraps diagnostics[0] into,
on top of this package).
import { parseThr } from 'flash-parser';
// parseThr/parseFlsh never throw on a malformed file — check the
// diagnostics array yourself (this is what the compiler's own
// CompileError wraps diagnostics[0] into, on top of this package).
const { diagnostics } = parseThr(`
<script>
field count: integer = 0
<component></component>
`); // missing </script>
for (const d of diagnostics) {
console.log(d.code, '—', d.message, `(line ${d.line})`);
}
// "thr/unterminated-script — No closing </script> found. (line 1)"
// ParseDiagnostic shape:
// { code: string, message: string, pos: number, end: number, line: number }
// pos/end are byte offsets into the source; line is 0-based.What this package owns
DSL grammar
The .thr <script>/template split, field/derived/state/read/watch, private/public function, the JS-shaped if/for/while/try, and .flsh classes (extends/override/super).
Full BrightScript grammar
A lexer, recursive-descent parser, and typed AST — independent of kopytko-brightscript-parser at parse time.
SceneGraph XML grammar
A lexer/parser/AST for the template markup and generated .xml output.
kopytko-brightscript-parser remains a dependency for two
narrow, non-parsing roles inside flash-theater-compiler
only (validating generated .brs post-codegen, and
supplying Roku's builtin-function name catalog) — never for parsing DSL source.
The BrightScript grammar
A full, self-sufficient BrightScript expression/statement grammar — used for every embedded region inside DSL source, and for BrightScript-level scope resolution.
import { tokenizeBrightScript, parseBrightScript, wrapBrightScriptNode } from 'flash-parser';
// A full, self-sufficient BrightScript grammar — vendored and adapted from
// kopytko-brightscript-parser, but parsed independently, not delegated to
// it at parse time. Used for every embedded expression/statement region
// inside DSL source.
const { root, diagnostics } = parseBrightScript(`
function add(a as Integer, b as Integer) as Integer
return a + b
end function
`);
const file = wrapBrightScriptNode(root);
console.log(diagnostics.length); // 0The SceneGraph XML grammar
A dedicated XML lexer/parser/AST for template markup and generated output — not a general-purpose XML library, just enough SceneGraph XML to round-trip losslessly.
import { parseSceneGraphXml } from 'flash-parser';
// A SceneGraph XML lexer/parser/AST — used for the template markup and
// for parsing generated .xml output back for validation. Returns the
// typed root XmlElement directly (undefined if the document has none).
const root = parseSceneGraphXml(`
<component name="Widget" extends="Group">
<children>
<Label id="label" text="hi" />
</children>
</component>
`);
console.log(root.tagName); // 'component'
console.log(root.attributes.map((a) => `${a.name}=${a.value}`));
// [ 'name=Widget', 'extends=Group' ]Full API surface
Beyond the examples above, the package also exports:
DSL AST nodes
ThrFile/FieldDeclaration/DerivedDeclaration/IfStatement/ClassDeclaration/... — one typed class per grammar construct, from ast.ts.
BrightScript AST + scopes
BsFunctionDeclaration/BsIfStatement/BsCallExpression/..., plus buildBrightScriptScopes/resolveBrightScriptName/findBrightScriptScopeAtLine.
Tokens & trivia
tokenize/TokenKind/Token/tokenFullText — the raw token stream with whitespace/comments attached, underneath the CST.
Visitor helpers
walk (a plain depth-first (node) => void callback over every CST node) and findAll (collect every node matching one SyntaxKind, wrapped into a typed AST class).
Embedded-region helpers
parseEmbeddedExpression/parseEmbeddedStatements/findTopLevelIdentifiers/findMemberAccesses/... — used by the compiler to analyze one BrightScript expression/statement region inside DSL source without reparsing the whole file.
The full, current export list is src/index.ts — every symbol there ships with its own TypeScript types.
Documentation
- Getting started — project layout and the compile CLI.
- GRAMMAR.md — the exact grammar this package implements.
- flash-theater-compiler — the compiler built on top of this parser.