Skip to content

Rune IR

Rune IR is Bifrost’s normalized, language-neutral representation of source structure. Every structural language adapter starts with a tree-sitter parse and emits the same vocabulary of kinds, roles, names, source spans, and containment relationships. The query_code matcher sees Rune IR rather than language-specific grammar nodes.

Rune IR and CodeQuery are separate representations on opposite sides of the matcher:

source -> language adapter -> Rune IR
RQL -> CodeQuery
matcher(CodeQuery, Rune IR)

Rune IR describes source. Rune Query Language describes a query and lowers into CodeQuery. Rune IR is not a raw tree-sitter tree, and CodeQuery is not a copy of source structure. This separation lets one normalized query match equivalent structure across several languages.

Internally, FileFacts is the arena-backed storage for one source file. Rune IR is the public, inspectable rendering of those facts.

The query REPL renders Rune IR for pasted source. It does not index a workspace. Start the REPL with bifrost --repl, then enter :ir <language>, paste the source, and finish with a line that contains only :end:

:ir rust
fn greet(name: &str) {
audit(name);
}
:end

The REPL prints a Rune IR (<label>): header, the rendered facts, a Starter RQL: header, and one starter query:

Rune IR (rust):
(function :range (0 41) :name "greet"
(identifier :range (3 8) :name "greet"
)
(identifier :range (9 13) :name "name"
)
(block :range (21 41)
(call :range (27 38) :name "audit"
(callee :span (27 32) :name "audit" :text "audit")
(args :span (33 37) :name "name" :text "name")
(identifier :range (27 32) :name "audit"
)
(identifier :range (33 37) :name "name"
)
)
)
)
Starter RQL:
(function :name "greet")

<label> is the language label that the REPL resolved from your input. For example, :ir c prints Rune IR (cpp):.

  • A kind form such as (function ...) or (call ...) is one fact. Nesting expresses containment: the block fact is inside the function, and the call fact is inside the block.
  • :range (start end) gives the fact’s source range.
  • A role form such as (callee ...) or (args ...) is a typed edge from the enclosing fact to a related source span. It is not a child fact and does not expose internal arena IDs. :span (start end) gives the related span.
  • :name is the fact’s or role target’s name when the adapter can state one. :keyword is the argument name of a kwargs role. :text is the exact source text of the role span.
  • Ranges and spans are zero-based, half-open UTF-8 byte offsets into the source you pasted.

The starter query uses the kind of the first top-level fact and its exact name when the fact has one. Bifrost parses the starter query with the RQL frontend before printing it. A Rune IR document can express containment and role edges that the starter query does not reproduce.

A Rune IR rendering is an inspection document, not a query. Save queries as .rql or canonical JSON. --query-file does not execute .rune files.

:ir accepts these labels. Matching ignores case, a leading ., -, and _. Run :languages in the REPL to print the label list.

You typeGrammar and dialectPrinted label
javaJavajava
goGogo
cpp, c++, or a C or C++ file extension (c, cc, cpp, cxx, h, hin, hpp, hh, hxx)C++ grammar, C++ rulescpp
cpp-cC++ grammar, C dialectcpp-c
javascript, js, mjs, or cjsJavaScript grammarjavascript
jsx, javascriptreact, or javascript-jsxTSX grammar, JavaScript adapter (the grammar Bifrost uses for .jsx files)jsx
typescript or tsTypeScript grammartypescript
tsx, typescriptreact, or typescript-tsxTSX grammar, TypeScript adaptertsx
python or pyPythonpython
rust or rsRustrust
phpPHPphp
scalaScalascala
csharp, c#, or csC#csharp
ruby or rbRubyruby
kotlin, kt, or ktsKotlinkotlin

c is a C++ file extension, so :ir c uses the cpp label. C and C++ share one tree-sitter grammar and one structural adapter. The cpp-c dialect changes how the indexed analyzer interprets declaration scopes in .c files. It does not change the Rune IR facts.

An unknown label prints unsupported Rune IR language and does not start a capture.

LimitValueWhat happens
Source size256 KiB (262,144 bytes)The REPL cancels the capture when the pasted source would exceed the limit.
Rendered facts10,000Rendering stops and prints (truncated "node limit reached").
Nesting depth128Rendering stops and prints (truncated "depth limit reached").
Source text copied into :name, :keyword, and :text values64 KiBRendering stops and prints (truncated "source byte limit reached").
Output size256 KiBRendering stops and prints (truncated "output byte limit reached").

The truncation form closes every open form, so a truncated document stays balanced. Rune IR input limits count full physical lines, including numeric initializer data.

Bifrost rejects input with an error and renders nothing when:

  • the source is empty;
  • the source contains a NUL byte;
  • a line is longer than the parser line-length limit, which is 16,000 bytes by default (the BIFROST_MAX_LINE_LENGTH environment variable changes it; see Environment Variables);
  • the adapter produces no facts for the source.

The VS Code extension has a Bifrost: Show Rune IR command. The language server runs that command, and the language server is now the separate bifrost-lsp program. The bifrost binary described here does not serve it. See LSP Server and VS Code LSP for its status.

These tables describe how the tree-sitter adapters feed the normalized query_code model. They are not query syntax. Query normalized kinds and roles such as call, assignment, callee, and right; tree-sitter node names stay behind the adapter boundary. For the meaning of each kind and role, see Code Query Vocabulary.

Every adapter follows the same pattern:

  • grammar node types become normalized kinds;
  • grammar fields become normalized roles;
  • expression helpers find terminal names, so service.run(...) is a call whose callee is run and whose receiver is service;
  • adapters skip facts they cannot model precisely, such as a declaration without a value as an assignment;
  • a query that uses a kind or role the adapter does not support returns an unsupported_structural_feature diagnostic instead of a guessed match.

yes means the adapter can produce facts that match the kind. - means a query for that kind returns an unsupported_structural_feature diagnostic for that language. A broad kind such as declaration, callable, literal, or loop matches its subtypes, so it is supported wherever one of its subtypes is.

Kindpythonjavajavascripttypescriptgocppcsharpphprubyrustscalakotlin
declarationyesyesyesyesyesyesyesyesyesyesyesyes
callableyesyesyesyesyesyesyesyesyesyesyesyes
functionyes-yesyesyesyesyesyesyesyesyesyes
methodyesyesyesyesyesyesyesyesyesyesyesyes
constructor-yesyesyes-yesyesyes---yes
lambdayesyesyesyesyesyesyesyesyesyesyesyes
classyesyesyesyesyesyesyesyesyesyesyesyes
importyesyesyesyesyesyesyesyesyesyesyesyes
parameter-yesyesyes--------
module-----yesyesyesyesyes--
callyesyesyesyesyesyesyesyesyesyesyesyes
concurrent_spawn----yes-------
resource_releaseyesyes----------
assignmentyesyesyesyesyesyesyesyesyesyesyesyes
field_accessyesyesyesyesyesyesyesyesyesyesyesyes
identifieryesyesyesyesyesyesyesyesyesyesyesyes
literalyesyesyesyesyesyesyesyesyesyesyesyes
string_literalyesyesyesyesyesyesyesyesyesyesyesyes
numeric_literalyesyesyesyesyesyesyesyesyesyesyesyes
boolean_literalyesyesyesyesyesyesyesyesyesyesyesyes
null_literalyesyesyesyesyesyesyesyesyes-yesyes
collection_literalyesyesyesyes-----yes--
jsx_element--yesyes--------
jsx_attribute--yesyes--------
jsx_spread_attribute--yesyes--------
object_property--yesyes--------
computed_property--yesyes--------
spread_element--yesyes--------
returnyesyesyesyesyesyesyesyesyesyesyesyes
throwyesyesyesyes-yesyesyes--yesyes
catchyesyesyesyes-yesyesyesyes-yesyes
ifyesyesyesyesyesyesyesyesyesyesyesyes
loopyesyesyesyesyesyesyesyesyesyesyesyes
for_loopyesyesyesyes--yesyesyesyesyesyes
while_loopyesyesyesyes-yesyesyesyesyesyesyes
decoratoryesyesyesyes--yesyes-yesyesyes
blockyesyesyesyesyesyesyes--yesyesyes

The JavaScript adapter reports parameter as supported for every JavaScript file. A parameter query matches formal parameters in .js, .mjs, .cjs, and .jsx files. A rest parameter (...args) and a single unparenthesized arrow parameter (x => x) are not parameter facts.

yes means a query can constrain the role in that language. - means the query returns an unsupported_structural_feature diagnostic, either for the role or for every kind that can carry it. A role is valid only on certain query kinds; for example, iterable is valid only on for_loop, and tag only on jsx_element. See Code Query Vocabulary for the kinds each role accepts.

Rolepythonjavajavascripttypescriptgocppcsharpphprubyrustscalakotlin
calleeyesyesyesyesyesyesyesyesyesyesyesyes
receiveryesyesyesyesyesyesyesyesyesyesyesyes
argsyesyesyesyesyesyesyesyesyesyesyesyes
kwargsyes-----yesyesyes-yesyes
leftyesyesyesyesyesyesyesyesyesyesyesyes
rightyesyesyesyesyesyesyesyesyesyesyesyes
operatoryesyesyesyesyesyesyesyesyesyesyesyes
moduleyesyesyesyesyesyesyesyesyesyesyesyes
decoratorsyesyesyesyes--yesyes-yesyesyes
objectyesyesyesyesyesyesyesyesyesyesyesyes
fieldyesyesyesyesyesyesyesyesyesyesyesyes
iterableyesyesyesyes-----yes--
elementsyesyesyesyes-----yes--
tag--yesyes--------
attributes--yesyes--------
children--yesyes--------
value--yesyes--------
key--yesyes--------
KindGrammar node types
callcall
field_accessattribute
functionfunction_definition
methodfunction_definition inside a class body (see below)
lambdalambda
classclass_definition
assignmentassignment
resource_releasewith_statement
importimport_statement, import_from_statement
identifieridentifier
string_literalstring, concatenated_string
numeric_literalinteger, float
boolean_literaltrue, false
null_literalnone
collection_literallist, set, dictionary, tuple
returnreturn_statement
throwraise_statement
catchexcept_clause
ifif_statement
for_loopfor_statement
while_loopwhile_statement
blockblock
decoratordecorator

A function_definition becomes method when its parent is a class body, including through a decorated_definition wrapper. An assignment node becomes an assignment fact only when it has a right-hand side, so x: int alone is not an assignment.

Roles: the function field of a call is the callee, arguments children are args, and keyword_argument nodes are kwargs. The object and attribute fields of an attribute node are object and field. Both import statements carry a module role. Decorators come from the surrounding decorated_definition wrapper.

Toy shape:

def run(code):
password = "hunter2"
audit(code)
KindGrammar node types
callmethod_invocation, method_reference, object_creation_expression
resource_releasetry_with_resources_statement
field_accessfield_access
methodmethod_declaration
constructorconstructor_declaration, compact_constructor_declaration
lambdalambda_expression
parameterformal_parameter, spread_parameter
classclass_declaration, interface_declaration, enum_declaration, record_declaration, annotation_type_declaration
assignmentvariable_declarator, assignment_expression
importimport_declaration
identifieridentifier, type_identifier, scoped_identifier, scoped_type_identifier
string_literalstring_literal
numeric_literaldecimal_integer_literal, hex_integer_literal, octal_integer_literal, binary_integer_literal, decimal_floating_point_literal
boolean_literaltrue, false
null_literalnull_literal
collection_literalarray_initializer
returnreturn_statement
throwthrow_statement
catchcatch_clause
ifif_statement
loopfor_statement
for_loopenhanced_for_statement
while_loopwhile_statement, do_statement
blockblock, switch_block
decoratorannotation, marker_annotation

A variable_declarator becomes an assignment fact only when it has a value.

Roles: the name field of method_invocation is the callee, the object field is the receiver, and the arguments field supplies positional args. object_creation_expression uses its type field as the call target. import_declaration carries a module role. annotation and marker_annotation nodes under modifiers become decorators. Java has no keyword arguments.

Toy shape:

class App {
void run(String code) {
String password = "hunter2";
audit(code);
}
}

JavaScript and TypeScript share one adapter. This table applies to both:

KindGrammar node types
callcall_expression, new_expression
field_accessmember_expression
functionfunction_declaration, function_expression, generator_function_declaration, generator_function
methodmethod_definition
constructormethod_definition named constructor
lambdaarrow_function
classclass, class_declaration
assignmentassignment_expression, variable_declarator
importimport_statement
identifieridentifier, property_identifier, private_property_identifier, shorthand_property_identifier, shorthand_property_identifier_pattern
collection_literalarray, object
jsx_elementjsx_element, jsx_self_closing_element
jsx_attributejsx_attribute
jsx_spread_attributejsx_expression whose only named child is a spread_element, directly inside an opening or self-closing JSX element
object_propertypair
computed_propertycomputed_property_name
spread_elementspread_element
string_literalstring, template_string
numeric_literalnumber
boolean_literaltrue, false
null_literalnull
returnreturn_statement
throwthrow_statement
catchcatch_clause
ifif_statement
loopfor_statement
for_loopfor_in_statement
while_loopwhile_statement, do_statement
blockstatement_block, switch_body
decoratordecorator

Plain JavaScript (.js, .mjs, and .cjs) has no parameter wrapper node. The JavaScript adapter maps a node that is a direct child of formal_parameters to parameter: identifier, assignment_pattern, object_pattern, and array_pattern. An identifier in that position is a parameter fact and not an identifier fact. rest_pattern under formal_parameters is not a parameter fact.

.jsx files use the TSX grammar, which wraps every parameter. For them, the JavaScript adapter also maps:

KindGrammar node types
identifiertype_identifier
parameterrequired_parameter, optional_parameter

TypeScript (.ts and .tsx) also maps:

KindGrammar node types
classabstract_class_declaration, interface_declaration, enum_declaration, type_alias_declaration
functionfunction_signature
methodmethod_signature, abstract_method_signature
parameterrequired_parameter, optional_parameter
identifiertype_identifier, nested_identifier

A TypeScript type alias is a class fact because it declares a name in the type namespace. A query for (class) therefore matches type aliases, interfaces, and enums. A rest parameter (...args) is not a parameter fact, because the grammar uses the same node for destructuring. A variable_declarator becomes an assignment fact only when it has a value.

Roles: the function field of call_expression, or the constructor field of new_expression, is the callee. When that target is a member_expression, its object is the receiver. member_expression supplies object and field. import_statement carries a module role, and decorator nodes attach to classes and class members. Assignments carry left and right. JSX elements carry tag, attributes, and children; attributes, object properties, computed properties, and spreads carry value; object properties carry key. JavaScript and TypeScript have no keyword arguments.

Toy shapes:

function run(code) {
const password = "hunter2";
audit(code);
}
function run(code: string): void {
const password = "hunter2";
audit(code);
}
KindGrammar node types
callcall_expression
concurrent_spawngo_statement
field_accessselector_expression
functionfunction_declaration
methodmethod_declaration
lambdafunc_literal
classtype_spec
declarationtype_alias
assignmentassignment_statement, short_var_declaration, var_spec, const_spec
importimport_declaration
identifieridentifier, field_identifier, package_identifier, type_identifier
string_literalinterpreted_string_literal, raw_string_literal
numeric_literalint_literal, float_literal
boolean_literaltrue, false
null_literalnil
returnreturn_statement
ifif_statement
loopfor_statement
blockblock, expression_case, type_case, communication_case, default_case, expression_switch_statement, type_switch_statement

A var_spec or const_spec becomes an assignment fact only when it has a value. A Go type alias (type A = B) is a declaration fact, not a class fact. Every Go for form is a loop; Go has no for_loop or while_loop facts.

Roles: a call’s function field is the callee. When the call target is a selector_expression, its operand field is the receiver. A selector’s operand and field fields are the field-access object and field. Imports use every import_spec path under an import_declaration. Go has no keyword arguments or decorators.

Toy shape:

func run(code string) {
var password = "hunter2"
audit(code)
}

C and C++ files share the cpp analyzer, structural adapter, and language-filter label.

KindGrammar node types
callcall_expression, new_expression
field_accessfield_expression
functionfunction_definition
methodfunction_definition inside a class or with a scoped name (see below)
constructorfunction_definition whose scope and name match a class (see below)
lambdalambda_expression
classclass_specifier, struct_specifier, union_specifier
declarationalias_declaration
modulenamespace_definition
assignmentassignment_expression, init_declarator
importpreproc_include
identifieridentifier, field_identifier, namespace_identifier, qualified_identifier, type_identifier, template_function, template_method, template_type, dependent_name, destructor_name, operator_name, primitive_type
string_literalchar_literal, string_literal, raw_string_literal
numeric_literalnumber_literal
boolean_literaltrue, false
null_literalnull
returnreturn_statement
throwthrow_statement
catchcatch_clause
ifif_statement
loopfor_statement, for_range_loop
while_loopwhile_statement, do_statement
blockcompound_statement, switch_statement

A function_definition inside a class, or one with a scoped name such as Widget::draw, is refined to method. A definition whose scope and name match, such as Widget::Widget, is refined to constructor. A using alias is a declaration fact. C files expose only the subset their syntax contains.

Roles: the function field of call_expression, or the type field of new_expression, is the callee. A field call uses the field expression’s argument as the receiver, and a qualified call uses the qualified scope as the receiver. preproc_include carries a module role. C and C++ have no keyword arguments or decorators.

Toy shape:

void run(const char* code) {
auto password = "hunter2";
audit(code);
}
KindGrammar node types
callcall_expression, macro_invocation
decoratorattribute; also an identifier inside a derive attribute
declarationfield_declaration
field_accessfield_expression
functionfunction_item, function_signature_item
methodfunction_item or function_signature_item inside an impl or trait block
lambdaclosure_expression
classstruct_item, enum_item, trait_item, type_item
modulemod_item
assignmentconst_item, static_item, let_declaration, assignment_expression, compound_assignment_expr
importuse_declaration
identifieridentifier, field_identifier, scoped_identifier, type_identifier
string_literalchar_literal, string_literal, raw_string_literal
numeric_literalinteger_literal, float_literal, negative_literal, and a unary_expression that negates a numeric literal
boolean_literalboolean_literal
collection_literalarray_expression, tuple_expression
returnreturn_expression
ifif_expression
looploop_expression
for_loopfor_expression
while_loopwhile_expression
blockblock

A Rust type alias is a class fact because it declares a name in the type namespace. A const, static, or let becomes an assignment fact only when it has a value. A negated numeric literal such as -1 is one numeric_literal fact; the inner literal is not a second fact. Rust has no null literal.

Roles: the function field of a call is the callee; generic functions are unwrapped to their terminal function name. A field-expression call target provides the receiver, and a scoped identifier exposes its path as the receiver. Macro invocations carry args from their token tree. use_declaration carries module roles for the imported path or alias. Outer attributes are attribute_item siblings that come before the item they annotate; each one adds a decorators edge to the item below it. Rust has no keyword arguments.

Toy shape:

fn run(code: &str) {
let password = "hunter2";
audit(code);
}
KindGrammar node types
callfunction_call_expression, member_call_expression, nullsafe_member_call_expression, scoped_call_expression, object_creation_expression
field_accessmember_access_expression, nullsafe_member_access_expression, scoped_property_access_expression, class_constant_access_expression
functionfunction_definition
methodmethod_declaration
constructormethod_declaration named __construct
lambdaanonymous_function, arrow_function
classclass_declaration, interface_declaration, trait_declaration, enum_declaration
modulenamespace_definition
assignmentproperty_element, const_element, assignment_expression, augmented_assignment_expression, reference_assignment_expression
importnamespace_use_declaration
decoratorattribute
identifiername, namespace_name, qualified_name, relative_scope, variable_name
string_literalstring, encapsed_string
numeric_literalinteger, float, and a unary_op_expression that signs a numeric literal
boolean_literalboolean
null_literalnull
returnreturn_statement
throwthrow_expression
catchcatch_clause
ifif_statement
loopfor_statement
for_loopforeach_statement
while_loopwhile_statement, do_statement

A namespace_definition is a module fact named by the last segment of its namespace name. A property_element or const_element becomes an assignment fact only when it has a value. PHP produces no block facts.

Roles: call target fields are the callee, object or scope fields are the receiver, argument nodes are positional args, and named arguments are kwargs. Namespace use declarations carry module roles, including aliases. PHP attributes are decorators.

Toy shape:

function run(string $code): void {
$password = "hunter2";
audit($code);
}
KindGrammar node types
callcall_expression, infix_expression, postfix_expression
field_accessfield_expression
functionfunction_definition, function_declaration, given_definition
methoda function fact inside a class, object, trait, or enum body
lambdalambda_expression
classclass_definition, object_definition, trait_definition, enum_definition, package_object, type_definition
assignmentval_definition, var_definition, assignment_expression
importimport_declaration
decoratorannotation
identifieridentifier, operator_identifier, type_identifier, stable_type_identifier
string_literalstring, interpolated_string_expression, character_literal
numeric_literalinteger_literal, floating_point_literal, and a prefix_expression that signs a numeric literal
boolean_literalboolean_literal
null_literalnull_literal
returnreturn_expression
throwthrow_expression
catchcatch_clause
ifif_expression
for_loopfor_expression
while_loopwhile_expression, do_while_expression
blockblock, indented_block, case_clause, extension_definition

A function inside a template body (including one inside an extension block within it) is refined to method. A named argument such as f(x = 1) is a kwargs entry, not an assignment fact.

Roles: generic functions are unwrapped to their terminal name, field-expression receivers are the receiver, and calls support positional args, named kwargs, and block-style arguments. Annotations are decorators.

Toy shape:

object App {
def run(code: String): Unit = {
val password = "hunter2"
audit(code)
}
}
KindGrammar node types
callinvocation_expression, object_creation_expression
field_accessmember_access_expression, conditional_access_expression (only when it has a member binding)
functionlocal_function_statement
methodmethod_declaration
constructorconstructor_declaration
lambdalambda_expression, anonymous_method_expression
classclass_declaration, interface_declaration, struct_declaration, enum_declaration, record_declaration
modulenamespace_declaration, file_scoped_namespace_declaration
declarationproperty_declaration
assignmentvariable_declarator, assignment_expression
importusing_directive
decoratorattribute
identifieridentifier, implicit_parameter, generic_name, qualified_name, alias_qualified_name, predefined_type
string_literalstring_literal, verbatim_string_literal, raw_string_literal, character_literal
numeric_literalinteger_literal, real_literal, and a prefix_unary_expression that signs a numeric literal
boolean_literalboolean_literal
null_literalnull_literal
returnreturn_statement
throwthrow_statement, throw_expression
catchcatch_clause
ifif_statement, switch_statement
loopfor_statement
for_loopforeach_statement
while_loopwhile_statement, do_statement
blockblock

A namespace is a module fact named by the last segment of a qualified namespace name. A switch statement is an if fact. A variable_declarator becomes an assignment fact only when it has a value.

Roles: invocation function targets and object-creation type targets are the callee. Member and conditional access targets provide receiver, object, and field. Arguments are positional args or named kwargs. Attributes are decorators, and using aliases are import module names.

Toy shape:

class App {
void run(string code) {
var password = "hunter2";
audit(code);
}
}
KindGrammar node types
callcall; also a bare identifier that Ruby evaluates as a method call
importa call to require, require_relative, load, or autoload with no receiver and a module argument
functionmethod
methodsingleton_method; also a method directly inside a class
lambdalambda, block, do_block
classclass, singleton_class
modulemodule
assignmentassignment, operator_assignment
field_accessscope_resolution
identifieridentifier, constant, instance_variable, class_variable, global_variable, self, simple_symbol, delimited_symbol, hash_key_symbol
string_literalstring
numeric_literalinteger, float, and a unary that signs a numeric literal
boolean_literaltrue, false
null_literalnil
returnreturn
catchrescue
ifif, unless
for_loopfor
while_loopwhile, until

A def directly inside a class is method. A def at the top level or inside a module stays function. The block or do_block that forms the body of a lambda is not a second lambda fact. Ruby produces no throw facts (raise is a call) and no block facts.

Roles: the call method field is the callee, the optional receiver is the receiver, ordinary arguments are args, and hash-pair arguments are kwargs. A static import string carries a module role. An interpolated string carries no module name. Ruby has no decorators.

Toy shape:

def run(code)
password = "hunter2"
audit(code)
end
KindGrammar node types
callcall_expression, constructor_invocation, infix_expression
field_accessnavigation_expression
functionfunction_declaration
methodfunction_declaration directly inside a class-like declaration
constructorprimary_constructor, secondary_constructor
lambdalambda_literal, anonymous_function
classclass_declaration, object_declaration, companion_object, object_literal, type_alias
assignmentproperty_declaration, assignment
importimport_header
decoratorannotation, file_annotation
identifiersimple_identifier, type_identifier
string_literalstring_literal, character_literal
numeric_literalinteger_literal, real_literal, hex_literal, bin_literal, long_literal, unsigned_literal, and a prefix_expression that signs a numeric literal
boolean_literalboolean_literal
null_literalnull_literal
returnjump_expression that is a return
throwjump_expression that is a throw
catchcatch_block
ifif_expression, when_expression
for_loopfor_statement
while_loopwhile_statement, do_while_statement
blockstatements

A break or continue jump is neither return nor throw. A property_declaration becomes an assignment fact only when it has a value; an uninitialized property stays a declaration. A when expression is an if fact. A Kotlin typealias is a class fact.

Roles: the adapter derives terminal callees and navigation receivers, and calls carry positional args, named kwargs, and trailing lambdas. Navigation expressions expose object and field. Initialized properties and assignments carry left and right. Imports carry the terminal and qualified module names. Annotations attach to declarations as decorators.

Toy shape:

fun run(code: String) {
val password = "hunter2"
audit(code)
}

Query normalized kinds and roles rather than the tree-sitter names listed above. Start from :ir <language>, copy the starter RQL, and refine it with the Rune Query Language guide. For complete executable examples, see the language tutorials. For the list of languages Bifrost analyzes, see Languages.