Skip to main content

SciLex — a generic, linear-time maximal-munch lexer built on REAL

Project description

SciLex

A small, header-only C++20 lexer built on REAL.

Define an ordered set of token rules — each a kind paired with a REAL regular expression — and SciLex tokenizes source text by maximal munch (longest match wins, rule order breaks ties). Because REAL is a linear-time engine, tokenization is linear and ReDoS-safe by construction: no token rule can make the scanner backtrack catastrophically.

This is a deliberate fresh start under the same premises as REAL — purity, simplicity, and measured optimality. SciLex is a thin layer over REAL, not a re-implementation of pattern matching.

Scope (v1)

Included:

  • Token rules: a kind, a real::regex, and a skip flag (whitespace, comments).
  • Maximal-munch tokenization with rule-order priority on equal-length ties.
  • Source positions: byte offset, 1-based line and byte column.
  • Non-owning tokens: each lexeme views into the source.
  • Two ways to consume tokens: tokenize (eager, into a vector) and scan (a lazy single-pass range that produces one token at a time — no token vector is allocated; the parser-friendly access pattern).
  • Optional synthetic end-of-input token (eof_policy::append): emits a final end_of_input token at the real end position, so a parser always has a current token to match.
  • Optional indentation layout (scilex::layout, an opt-in header): rewrites a token stream with synthetic newline / indent / dedent tokens for indentation-significant languages; throws layout_error on an inconsistent dedent.
  • Lexical errors as exceptions carrying the failing position (lex_error).
  • Linear-time / ReDoS-safe tokenization, inherited from REAL.

Not in v1 (and honestly excluded — no phantom features):

  • Modes / context-sensitive lexing.
  • Compile-time static_lexer (built on REAL's static_regex).
  • Codepoint columns (columns are byte-based, matching REAL's UTF-8 model).
  • Command-line tool, JSON specification language.

These may come later, each only if it earns its place — measured, tested, and kept minimal.

Dependencies

SciLex is header-only and depends only on REAL's headers. By default the build looks for them in a sibling checkout:

~/Projects/
├── real-v1/      # REAL
└── scilex-v1/    # SciLex  (uses ../real-v1/include by default)

Point the build elsewhere with REAL_INCLUDE (Makefile) or -DSCILEX_REAL_INCLUDE=... (CMake) — for instance at the path printed by python -c "import real; print(real.get_include())" when REAL is installed via pip.

For CI or a reproducible build — where no on-disk layout can be assumed — fetch REAL with CMake FetchContent instead (make build FETCH=1, or -DSCILEX_FETCH_DEPS=ON); point it at a remote and pin a tag with -DSCILEX_REAL_REPO=https://… -DSCILEX_REAL_TAG=v2026.6.6.

Build

make test        # build and run the test suite
make coverage    # line-coverage summary + HTML report
make sanitize    # tests under AddressSanitizer + UndefinedBehaviorSanitizer
make lint        # clang-tidy
make format      # uncrustify, in place
make doc         # API reference (Doxygen) with embedded coverage

Override the compiler with make test CXX=g++-14.

scilex::scilex is the CMake target — add_subdirectory, FetchContent, or an installed config package. The config calls find_dependency(real), so installing REAL's config package alongside (on the same prefix) makes the whole chain resolve from one find_package:

# With REAL and SciLex installed under <prefix>:
find_package(scilex CONFIG REQUIRED)   # pulls in real:: transitively
target_link_libraries(app PRIVATE scilex::scilex)

Releasing

make release computes the next calendar version YYYY.M.PATCH — the patch resets each month, the first release of a month is .0 (PEP 440 drops leading zeros, so 2026.6.1, never 2026.06.001) — bumps it in pyproject.toml and python/scilex/__init__.py, then commits, tags and pushes from a clean main. The pushed tag drives .github/workflows/release.yml, which checks the tag matches the version, builds abi3 wheels (cibuildwheel, Linux/macOS/Windows) and the self-contained sdist, and publishes to PyPI via Trusted Publishing (OIDC, no stored secret). It then populates the GitHub /releases page with auto-generated notes and the built artifacts. The pushed tag is the single thing that triggers a publish; SciLex remains consumable as source too (sibling checkout / FetchContent / get_include()).

One-time PyPI setup. Publishing needs a PyPI Trusted Publisher configured once for the project (publisher RECHE23/scilex, workflow release.yml, environment pypi) and a matching pypi GitHub environment — no API token is stored.

Python binding

SciLex ships an abi3 CPython extension (use the C++ lexer from Python). pip install scilex installs one cp310-abi3 wheel per platform (CPython 3.10+; the self-contained sdist compiles where no wheel matches, pulling REAL's headers from the real-regex build dependency). For a source checkout:

make python        # build the extension in place
make python-test   # run the binding test suite
import scilex
lx = scilex.Lexer([
    (0, r"\s+", True),                       # (kind, pattern, skip) — skipped
    (1, r"[0-9]+", False),                   # number
    (2, r"[A-Za-z_][A-Za-z0-9_]*", False),   # identifier
])

# Eager: a list of rich Token objects (kind, lexeme, structured position).
[(t.kind, t.lexeme) for t in lx.tokenize("foo 42")]      # [(2, 'foo'), (1, '42')]

# Lazy: a generator yielding one Token at a time — nothing else is held.
for tok in lx.scan("foo 42"):
    tok.kind, tok.lexeme, tok.position.line, tok.position.column

# A lexical error carries the failing position.
try:
    lx.tokenize("foo @")
except scilex.error as e:
    e.position                                            # Position(line=1, column=5, offset=4)

# eof=True appends a terminal END_OF_INPUT token (a parser always has a token).
lx.tokenize("42", eof=True)[-1].kind == scilex.END_OF_INPUT

For indentation-significant languages, Layout rewrites an eof=True token stream with NEWLINE / INDENT / DEDENT tokens read from each line's leading column:

lx = scilex.Lexer([(0, r"\s+", True), (1, r"\w+", False), (2, r":", False)])
laid = scilex.Layout().apply(lx.tokenize("if x:\n    a\nb", eof=True))
[t.kind for t in laid]
# [1, 1, 2, NEWLINE, INDENT, 1, NEWLINE, DEDENT, 1, NEWLINE, END_OF_INPUT]

scilex.get_include() returns the header directory so a C++ project can compile against SciLex located through its Python install (add real.get_include() too, since SciLex's headers include REAL's).

Example

#include <scilex/scilex.hpp>
#include <vector>

enum kind { WS, KW_IF, ID, NUM, PLUS };

std::vector<scilex::rule> rules;
rules.push_back({WS,    real::regex("\\s+"),  true}); // skipped
rules.push_back({KW_IF, real::regex("if")});          // before ID: wins ties
rules.push_back({ID,    real::regex("[a-z]+")});
rules.push_back({NUM,   real::regex("[0-9]+")});
rules.push_back({PLUS,  real::regex("\\+")});

const scilex::lexer lexer(std::move(rules));

// Eager: all tokens in a vector.
for (const scilex::token& t : lexer.tokenize("if x + 42")) {
    // t.kind, t.lexeme, t.start.{offset,line,column}
}

// Lazy: one token at a time, nothing else materialized.
for (const scilex::token& t : lexer.scan("if x + 42")) {
    // ...
}

Performance

See BENCHMARKS.md for a reproducible, honest baseline (make bench). The short of it: on benign input Python's re is faster (a mature C backtracking engine), but SciLex is linear-time and ReDoS-safe by construction — on a pathological pattern like (a+)+b it stays flat (~78 µs at 1000 chars) where re explodes exponentially (seconds, then never finishing). SciLex trades raw throughput on easy inputs for a guarantee that holds on every input.

License

MIT — see LICENSE.

Author

René Chenard — rene.chenard@gmail.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scilex-2026.6.0.tar.gz (29.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

scilex-2026.6.0-cp310-abi3-win_amd64.whl (242.2 kB view details)

Uploaded CPython 3.10+Windows x86-64

scilex-2026.6.0-cp310-abi3-win32.whl (240.3 kB view details)

Uploaded CPython 3.10+Windows x86

scilex-2026.6.0-cp310-abi3-musllinux_1_2_x86_64.whl (1.5 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

scilex-2026.6.0-cp310-abi3-musllinux_1_2_aarch64.whl (1.4 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

scilex-2026.6.0-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (424.9 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ x86-64manylinux: glibc 2.28+ x86-64

scilex-2026.6.0-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl (416.7 kB view details)

Uploaded CPython 3.10+manylinux: glibc 2.24+ ARM64manylinux: glibc 2.28+ ARM64

scilex-2026.6.0-cp310-abi3-macosx_11_0_x86_64.whl (55.1 kB view details)

Uploaded CPython 3.10+macOS 11.0+ x86-64

scilex-2026.6.0-cp310-abi3-macosx_11_0_arm64.whl (52.7 kB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file scilex-2026.6.0.tar.gz.

File metadata

  • Download URL: scilex-2026.6.0.tar.gz
  • Upload date:
  • Size: 29.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for scilex-2026.6.0.tar.gz
Algorithm Hash digest
SHA256 6e2f20058b48600c91ca4bb37e2e59c0bd914955bfd161a292e162ba4e037b21
MD5 8b0e10a6d53ac61c8a1fdf5ef4373fec
BLAKE2b-256 0f0b11201844b136ab8a3192864a4676e5de2dab3921ee3376a9b6637ad353b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0.tar.gz:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: scilex-2026.6.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 242.2 kB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 bb5b07bafb0dbb02bd8b80b1b6e0dd48eb971d71424075b324af2edb23a815a6
MD5 42289446ec9752646a468e0d1b3e48fe
BLAKE2b-256 89b4d318a7561560d8ce13f8a1921fb9ba3e27c34ec800cffa9e9e72ddac37b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-win_amd64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-win32.whl.

File metadata

  • Download URL: scilex-2026.6.0-cp310-abi3-win32.whl
  • Upload date:
  • Size: 240.3 kB
  • Tags: CPython 3.10+, Windows x86
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-win32.whl
Algorithm Hash digest
SHA256 69c313ceea96a6437d990da2df3ff143c5238d1e467f3327a0d7b79f68bc8503
MD5 59e2a58c0f3681a2cafecfca46a2a117
BLAKE2b-256 55172de6cf5c8510fb600f8d82549b22092ea53e9d225ef2defc96ce59f091d4

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-win32.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 9dd56525035cec07ff7c64f35e3f1f032ad92b4946436ba18d7076b81c2ab1ae
MD5 58d8c371197e6bf164f288be86c91961
BLAKE2b-256 8fd836909fe852885b799bd41a4c95a92232a6ba10febb884caf1ef48de7f636

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-musllinux_1_2_x86_64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 6833d7a990fadd4fa9a47d117932810a837ac5c98d4b18385cced5623d846937
MD5 dcb2e447e64e3a1ffb1e1390b27182ac
BLAKE2b-256 538ede7a912fa33cff5196975cb93814d11e59d6a8d4fc531a89ee397647579c

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-musllinux_1_2_aarch64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 6948c41228bf0fb08296fb401acefe79b006492b56cbc536983b5210a637e058
MD5 2e5fc8ae0be2c0c003e3ffca94c78416
BLAKE2b-256 77f0774612c8b502b02acb3f2c393e18da2860ce03924aadd5639dd481611302

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 3e5b56e0b5f5f10f38b466a65a09016ccba43a31a8f4582e8b1bdd9099b69a65
MD5 83c684dc723c2deff3888d70f6c34d2f
BLAKE2b-256 1c2de1cfbae08fd63dbe2c734256c292db0f702b4604c89ef9fea58676ef0cb4

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-macosx_11_0_x86_64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-macosx_11_0_x86_64.whl
Algorithm Hash digest
SHA256 874bcb7429f54a57bbdeb02e8e29164bf78fa48c0f5f6982d0fbfb9b49171b8b
MD5 61dc5e1e130308d1ceaa6ddc59594446
BLAKE2b-256 3f1491afffd986d84523503511f71f04dda51c53838d4909a2b467401adaabde

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-macosx_11_0_x86_64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file scilex-2026.6.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for scilex-2026.6.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 2384c662ee6823248dd5b092dbb3078907fd1757d4a8fc649c978b1515a299fb
MD5 e88aaaf08661ab95c3a9340d7bb3becc
BLAKE2b-256 ae475d6f7ac8179ace38551be6c2752cf6f2fe5559a87d1ccdb5bfbb81d4c7b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for scilex-2026.6.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on RECHE23/scilex

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page