flexr

CI License: MIT

Product site · Playground · Documentation

Ruby-native lexer generator for parser authors who want ordinary Ruby specifications and deterministic generated output.

Write one specification, run it directly while developing, or generate Ruby for deployment when startup cost and build reproducibility matter. Both modes use the same rules and actions.

Why flexr?

  • Ordinary Ruby DSL; no separate lexer language is required.
  • Leftmost-longest matching: the longest rule wins, and source order breaks equal-length ties.
  • Runtime/generated parity with diagnostics for unsupported or risky designs.

Quick start

gem install flexr
# lexer.flexr.rb
require "flexr"

class Lexer < Flexr::Lexer
  emits :INTEGER, :PLUS

  rule(/[ \t\r\n]+/, skip: true)
  rule(/[0-9]+/) { emit :INTEGER, text.to_i }
  rule(/\+/) { emit :PLUS }
end

Lexer.new("12 + 3").tokens
# => [[:INTEGER, 12], [:PLUS, "+"], [:INTEGER, 3]]

Follow the calculator tutorial for validation, generation, and runtime/generated comparison.

How matching works

At each input position flexr considers every active rule and chooses the longest match. If multiple rules consume the same number of bytes, the rule defined first wins. A rule can use followed_by: to inspect trailing context without consuming it.

The regexp engine is a DFA-oriented subset of Ruby regexp syntax. See the regexp reference before relying on look-around, backreferences, or other non-regular constructs.

Runtime or generated?

Mode Build requirement Runtime requirement Best for
Runtime flexr gem flexr gem Development, tests, and dynamic Ruby specs
Generated flexr plus Prism on Ruby 3.3+ flexr gem Reproducible deployment artifacts
Standalone generated flexr plus Prism on Ruby 3.3+ Generated file and Ruby standard library Distribution without the gem

Static generation is the default. Use --eval only for trusted specifications; it executes the specification during the build. See the generation guide and standalone deployment guide.

Is flexr right for you?

flexr fits projects that want a Ruby-native lexer, deterministic longest-match semantics, Unicode-aware byte-level matching, and parser integration. It is not a drop-in replacement for a first-match lexer, and it does not accept regexp features that require backtracking or capture-dependent matching.

Documentation

Start with the documentation map, then choose the path that matches your task:

  • Tutorial — build one lexer from source to generated artifact.
  • How-to guides — solve one focused integration or runtime problem.
  • Reference — look up APIs, CLI options, regexp support, diagnostics, and compatibility.
  • Explanation — understand matching, backends, Unicode, generation, and security decisions.
  • Examples — executable specifications and parser integrations.

Compatibility and stability

Runtime Ruby support starts at 3.1. The generator requires Ruby 3.3 or newer because it uses Prism. Stable and experimental APIs are listed in the public API contract. The vendored Unicode snapshot and generated-artifact policy are described in the compatibility reference.

Security

Lexer actions are Ruby code and remain Ruby code in generated output. Static generation parses the specification, while --eval executes it. Treat source specifications and generated files as trusted build inputs; never process an untrusted specification with --eval.

Contributing

Run bundle exec rake docs:verify together with the normal test and generated artifact checks before submitting changes. See CONTRIBUTING.md and RELEASING.md.

License

MIT. See LICENSE.txt.