Ordinary Ruby specifications
Use classes, constants, interpolation, and familiar Ruby actions without learning a separate lexer language.
flexr lets you write a lexer specification as ordinary Ruby, run it while developing, inspect its matching behavior, and generate deterministic Ruby for deployment.
class Lexer < Flexr::Lexer
rule(/==/) { emit :EQ }
rule(/=/) { emit :ASSIGN }
rule(/if/) { emit :IF }
rule(/[a-z_][a-z0-9_]*/) { emit :IDENT }
end Keep the feedback loop in Ruby. When the lexer is ready to ship, compile the same rules into a predictable artifact and keep the behavior easy to reason about.
Use classes, constants, interpolation, and familiar Ruby actions without learning a separate lexer language.
Longest match wins. If matches have the same length, the rule defined first wins. The contract stays visible.
Develop with the runtime interpreter, then compare its token stream with generated Ruby before deployment.
Find unsupported regexp constructs, unreachable rules, undeclared tokens, and other problems before they reach a parser.
Define rules and actions in a Ruby class that looks like the code around it.
Check the specification and make a matching decision visible when rules compete.
Produce deterministic Ruby for environments where startup and deployment shape matter.
Return tokens to Racc, Lrama, or your own parser protocol with explicit locations.
Both modes consume the same lexer design. The choice is about the deployment boundary, not a second language.
Load the specification directly with the flexr runtime. It is a strong fit for iteration, tests, and applications that value a compact source of truth.
Compile the specification to Ruby and commit or build the generated artifact. Use standalone output when the deployed process should not load the gem at runtime.
flexr is designed to be inspectable. Know what runs at build time, what remains Ruby code, and what the regexp compiler accepts.
Only run specifications and generated artifacts you trust. Static checks do not make arbitrary actions safe.
Check the compatibility matrix before porting lookarounds, backreferences, or open-ended repetition.
firstmatch is experimental because it changes the usual longest-match contract and needs an explicit option.
Inspect examples, diagnostics, generated output, and the benchmark log in the repository.