Convention layer defining what it means to be a "language" in the Roux ecosystem.
Languages implement this behaviour to specify file extensions, query registrations,
and entry points. Roux.Lang is not required to use Roux — it's a convenience for
the common case of building compilers.
Usage
defmodule MyLang do
@behaviour Roux.Lang
use Roux.Query
@impl true
def file_extensions, do: [".ml"]
@impl true
def compile_query, do: :compile
@impl true
def register_queries(db), do: Roux.Lang.register_module(db, __MODULE__)
definput :source_text, durability: :low
defquery :parse, key: file_path do
source = input(db, :source_text, file_path)
MyLang.Parser.parse(source)
end
defquery :compile, key: file_path do
ast = query(db, :parse, file_path)
MyLang.Codegen.compile(ast)
end
endRegistration
Languages are registered with a database via register/2:
db = Roux.Database.new()
Roux.Lang.register(db, MyLang)Registration calls register_queries/1 on the language module, then records
file extension mappings. Extension conflicts raise ArgumentError.
Cross-language interfaces
When multiple languages coexist, they may need to reference each other's exports.
The optional module_interface/2 callback enables this — resolve_interface/2
dispatches to the correct language based on file extension.
Summary
Callbacks
The query name that compiles a single file. Called by the Mix compiler.
Query that produces completions at a position.
Query that produces go-to-definition results.
Query that produces diagnostics for a file.
Query that produces document symbols for the outline.
File extensions this language handles (e.g., [".myex", ".myl"]).
Query that produces hover info at a position.
Display name for this language (e.g., "Lark").
Line comment prefixes for this language (e.g., ["# "]).
Return the public interface of a compiled module.
Called after source discovery, before compilation begins.
Register all queries for this language with the database.
Functions
Looks up the language module registered for a file extension.
Returns the display name for a language module.
Returns the line comment prefixes for a language module.
Registers a language with the database.
Registers a module's queries and inputs with the database.
Returns all language modules registered with the database.
Resolves the public interface of a source file by dispatching to the
appropriate language's module_interface/2 callback.
Recursively walks a directory, returning all file paths.
Types
Callbacks
@callback compile_query() :: atom()
The query name that compiles a single file. Called by the Mix compiler.
@callback completions_query() :: atom()
Query that produces completions at a position.
@callback definition_query() :: atom()
Query that produces go-to-definition results.
@callback diagnostics_query() :: atom()
Query that produces diagnostics for a file.
@callback document_symbols_query() :: atom()
Query that produces document symbols for the outline.
@callback file_extensions() :: [String.t()]
File extensions this language handles (e.g., [".myex", ".myl"]).
@callback hover_query() :: atom()
Query that produces hover info at a position.
@callback language_name() :: String.t()
Display name for this language (e.g., "Lark").
@callback line_comments() :: [String.t()]
Line comment prefixes for this language (e.g., ["# "]).
@callback module_interface(Roux.Database.t(), source_path()) :: module_interface()
Return the public interface of a compiled module.
@callback prepare(Roux.Database.t(), [source_path()]) :: :ok
Called after source discovery, before compilation begins.
Receives the database and the list of source paths belonging to this language. Use this to populate inputs that depend on knowing all source files (e.g., module registries that map module names to file paths).
@callback register_queries(Roux.Database.t()) :: :ok
Register all queries for this language with the database.
Functions
@spec lang_for_extension(Roux.Database.t(), String.t()) :: {:ok, module()} | :error
Looks up the language module registered for a file extension.
Returns {:ok, module} if found, :error otherwise.
Returns the display name for a language module.
Falls back to the last segment of the module name
(e.g., Lark → "Lark", MyApp.HoverLang → "HoverLang").
Returns the line comment prefixes for a language module.
Falls back to ["# "] if the module does not implement line_comments/0.
@spec register(Roux.Database.t(), module()) :: :ok
Registers a language with the database.
Calls lang_module.register_queries(db) to register all queries, then records
file extension mappings in the input registry using {:__roux_lang__, extension}
tuple keys.
Raises ArgumentError if an extension is already claimed by a different language.
Idempotent — registering the same language twice is a no-op.
@spec register_module(Roux.Database.t(), module()) :: :ok
Registers a module's queries and inputs with the database.
Reads the module's __roux_queries__/0 metadata (generated by use Roux.Query)
and registers each input definition via Roux.Input.register/2 and each query
definition via Roux.Database.register_query/3, with its code version
(Roux.Query.code_version/2).
Intended to be called from a language's register_queries/1 callback.
Raises ArgumentError for a module that is not available or does not
use Roux.Query, and Roux.Query.FormatError for one compiled
against a roux of another definition format (Roux.Query.format/0):
none of its code runs.
@spec registered_languages(Roux.Database.t()) :: [module()]
Returns all language modules registered with the database.
@spec resolve_interface(Roux.Database.t(), source_path()) :: module_interface()
Resolves the public interface of a source file by dispatching to the
appropriate language's module_interface/2 callback.
Raises ArgumentError if no language is registered for the file's extension.
Recursively walks a directory, returning all file paths.
Returns an empty list if the directory does not exist or is unreadable.