Skip to main content

pyRegTab

CI PyPI Python License: MIT

RegTab: pattern-driven data extraction from document tables with regular structure — the Python port of jRegTab with a native Rust core.

pyRegTab compiles RTL (Regular Table Language) patterns into abstract table patterns (ATP), matches them against a table's syntactic layer (ITM), and interprets the match into a relational recordset:

TableSyntax → RtlCompiler/TablePattern → AtpMatcher → TableInterpreter → Recordset

pyRegTab 0.7.1 ≙ jRegTab 0.7.1 (same API, same semantics, same test corpus), including the embedded RTL DSL pyregtab.dsl — a port of jRegTab's ru.icc.regtab.dsl (added upstream in jRegTab 0.3.0). Python-side extras on top of the Java API: AtpMatcher.match_many (parallel batch matching), Recordset.to_pandas(), Recordset.to_csv(), RtlCompileError.line/.col attributes, CellDerivedItem.span (the item's byte range within the raw cell text), and the CLI runner python -m pyregtab.runner (see below).

Since 0.5.1 the language and the interpreter follow jRegTab 0.6.0–0.7.1: JOIN(K) is the record product (a cross product for K = ∅, an equi-join on the key K otherwise), the former folding operation is spelled CONCAT(K) — a pattern written for 0.5.x must replace JOIN(K) by CONCAT(K); the key K may name attributes (CONCAT(0, 1, 'A', 'B'), JOIN('Year')); skipped CONCAT/JOIN actions (a key mismatch, a shared attribute, a forgotten REC) are reported through TableInterpreter.diagnostics(); zero-width subrows match the empty sequence and {0}/{1} are accepted; cell-derived providers use a spatial index, so interpretation is linear in the number of cells (a 1.19 M-cell table interprets in seconds).

Installation

pip install pyregtab

Binary wheels are published for Windows, Linux and macOS (x86-64 / arm64), CPython ≥ 3.10 (one abi3 wheel per platform). Building from the sdist requires a Rust toolchain.

Example

from pyregtab import TableSyntax, RtlCompiler, AtpMatcher, TableInterpreter

syntax = TableSyntax(3, 3)
syntax.cell(0, 1).set_text("CA");  syntax.cell(0, 2).set_text("HU")
syntax.cell(1, 0).set_text("IKT"); syntax.cell(1, 1).set_text("0 Jan"); syntax.cell(1, 2).set_text("8 Feb")
syntax.cell(2, 0).set_text("SVO"); syntax.cell(2, 1).set_text("31 Jan"); syntax.cell(2, 2).set_text("40 Feb")

pattern = RtlCompiler.compile("""
    [ [] [VAL : 'AIRLINE'->AVP]+ ]
    [ [VAL : 'AIRPORT'->AVP]
      [VAL : (COL, ROW, CL)->REC, 'ND'->AVP " " VAL : 'MON'->AVP]+ ]+
""")

itm = AtpMatcher.match(pattern, syntax)     # InterpretableTable | None
rs = TableInterpreter().interpret(itm)      # Recordset
rs.schema.attributes                        # ['ND', 'AIRLINE', 'AIRPORT', 'MON']
rs[0]["ND"]                                 # '0'
df = rs.to_pandas()                         # extras: pip install pyregtab[pandas]

Patterns can also be built without RTL, via the fluent spec API (TablePattern.of(SubtablePattern.of(...)) — same factories as in Java, snake_case method names), and serialized back to RTL with AtpToRtlSerializer.serialize(pattern).

For a terser, RTL-like way to build patterns in code, use the embedded RTL DSL (pyregtab.dsl) — see Embedded RTL below.

Named Python predicates are attached to RTL via EXT('name'):

from pyregtab import Bindings

p = RtlCompiler.compile(
    "{ [ [EXT('isTotal') ? VAL : ST*->REC] []+ ] }+",
    Bindings.of().cell("isTotal", lambda cell: cell.text.startswith("Total")),
)

Embedded RTL

The pyregtab.dsl module is a fluent DSL that reads almost like RTL but is ordinary Python — with IDE completion, structural typing, pattern composition via plain variables, and Python callables as escape-hatch constraints. It builds the same TablePattern objects as the compiler (verified byte-for-byte against RtlCompiler.compile for a representative set of tasks in tests/test_dsl.py).

from pyregtab.dsl import *

# RTL: { [ [VAL : ST*->REC] [VAL]{2} []+ ]
#        [ []               [VAL]{4} []+ ] }+
p = table(
    subtable(
        row(cell(VAL, rec(ST.unbounded())), cell(VAL).exactly(2), skip().one_or_more()),
        row(skip(),                         cell(VAL).exactly(4), skip().one_or_more()),
    ).one_or_more())

Method names are snake_case (.one_or_more(), .and_(), .split_by()); the vocabulary constants (VAL, ST, COL, C(n), …) match RTL. See the Embedded RTL guide for the full mapping and the where(...) escape hatch.

API mapping (Java → Python)

Java Python
RtlCompiler.compile(String) RtlCompiler.compile(str) / pyregtab.compile(...)
AtpMatcher.match(p, s) → Optional<InterpretableTable> AtpMatcher.match(p, s) → InterpretableTable | None
Quantifier.oneOrMore() Quantifier.one_or_more()
new TableInterpreter().withStrategy(s).interpret(itm) TableInterpreter().with_strategy(s).interpret(itm)
rs.records().get(0).get("Name") rs[0]["Name"], rs.records, record.get("Name")
cell.text() / cell.setText(t) property cell.text (get/set); cell.set_text(t) also works
RtlCompileException RtlCompileError

Architecture

Everything after the Python call boundary runs in a native core written in Rust (pyregtab._core, built with PyO3 and maturin); the Python layer is a thin re-export.

  • grammar/RTL.g4 — the normative specification of the RTL language (a verbatim copy from jRegTab; the upstream commit and the grammar's SHA-256 are recorded in grammar/UPSTREAM). The core's parser is a hand-written lexer + recursive descent that structurally follows the grammar rules. A CI job (tools/check_grammar_sync.py) fails the build if the copy drifts from the pinned hash, and — when a jRegTab read token is available — cross-checks it byte-for-byte against the upstream commit.
  • conformance/ — the shared RTL conformance corpus (also pinned from jRegTab, see conformance/UPSTREAM and conformance/README.md). Both implementations must compile every positive case to the same canonical form and reject every negative case; the corpus runs in CI of both projects. Any RTL language change flows: RTL.g4 in jregtab → corpus extension → both parsers → green corpus in both CIs.
  • Regular expressions in RTL constraints are executed by the Rust regex crate (linear-time). The reference fixture corpus uses no lookaround/backreferences (audited), so the dialect is compatible with java.util.regex on this corpus. Documented divergences from Java: \d/\s/\w are Unicode-aware in regex (ASCII in Java), and SUBSTR indices count code points (UTF-16 units in Java) — identical behavior on the entire reference corpus.

Testing

pytest tests runs (1 981 tests):

  • the full benchmark suite — tasks 001–150 (Foofah, RegTab, Baikal), every fixture variant, both via RTL patterns and via ATP patterns built with the Python spec API (1 500 task variants in total; fixtures are copied verbatim from jRegTab into tests/fixtures/tasks, ATP builders are mechanically translated from the Java tests by tools/translate_atp.py);
  • embedded RTL DSL parity — 26 representative tasks/constructs built with pyregtab.dsl produce byte-identical ATP to RtlCompiler.compile (tests/test_dsl.py);
  • the RTL conformance corpus (positive canonical forms, fixed points, negative rejections);
  • RTL↔ATP round-trip for tasks 001–050;
  • API unit tests (syntax layer, extractors, EXT bindings, custom predicates, transformations, interpreter options, GIL-released batch matching from a thread pool and via AtpMatcher.match_many);
  • CONCAT/JOIN semantics, named keys, diagnostics, strict preconditions, zero-width subrows and {0}/{1} (tests/test_join_concat.py, a port of jRegTab's TableInterpreterMultiRecordTest).

cargo test additionally runs the conformance corpus, an end-to-end smoke test against the native core alone, the working-state unit tests for CONCAT/JOIN (ports of WorkingStateConcatTest/WorkingStateJoinTest), the zero-width matcher cases, and a randomized equivalence test of the indexed provider against the reference definition Υ^{J,k}_{τ,κ} (full scan + sort). Differential testing against the Java reference (tools/differential.py + tools/RecordsetDumpMain.java) compares recordsets cell-by-cell on all 750 task variants; the 244 ATBench solutions of regtab-eval-on-atbench are byte-identical between the jRegTab 0.7.1 runner and python -m pyregtab.runner.

IDE support

Install Regular Table Language (RTL) from the VS Code Marketplace (ext install regtab.regtab): syntax highlighting for .rtl files and for RTL embedded in Python strings passed to RtlCompiler.compile(...), plus compile diagnostics and a live match preview against CSV fixtures. The extension sources are at regtab/vscode-rtl; a TextMate bundle for IntelliJ/PyCharm and other TextMate editors is under ide/.

RTL is also validated at compile time: RtlCompiler.compile(...) raises RtlCompileError with a line:col position on an invalid pattern.

Command-line runner

python -m pyregtab.runner <solution.rtl> (or python -m pyregtab <solution.rtl>) reads ./input.csv, applies the pattern (RtlCompiler.compile → AtpMatcher.match → TableInterpreter with RECORD_FIRST → pattern.transform) and writes ./output.csv — the schema as the header row, every field double-quoted, a missing value as the empty string, UTF-8, LF. It is the drop-in counterpart of the regtab-runner jar used by regtab-eval-on-atbench: same I/O contract, same exit codes (0 — success, 1 — the pattern did not match, 2 — invalid usage), same CSV parsing (RFC 4180 with multi-line quoted cells, ragged rows padded); on the 244 ATBench solutions of that project the two runners produce byte-identical output.csv. --input/--output override the file names, --strict turns interpreter diagnostics into errors.

Development

python -m venv .venv && . .venv/bin/activate   # or .venv\Scripts\activate
pip install maturin pytest
maturin develop --release
pytest tests -q

License

MIT

Metadata

Release files for pyregtab 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for pyregtab 0.7.1
File
pyregtab-0.7.1-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
pyregtab-0.7.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
pyregtab-0.7.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
pyregtab-0.7.1-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
pyregtab-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 6.8 MB

Release files / pyregtab-0.7.1-cp310-abi3-win_amd64.whl

Download URL pyregtab-0.7.1-cp310-abi3-win_amd64.whl
Size 1.3 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
b13e0a1ccdaa1aca4229b0f8485c01a4d1e82c2b9f1cd44f77577e62848172cc
BLAKE2b-256 checksum
How to use checksums
929be268f52b4e0770ec9017a4f2800579fb1989c6a0a7506a8cb13b31de6a79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyregtab-0.7.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL pyregtab-0.7.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 1.4 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
edc3f6f03059aaadea8051b01c3d26b99bb26c92742ee9b538995243e90a5b2e
BLAKE2b-256 checksum
How to use checksums
a6f8107895bb41b849ef4738ba247504f27806cbbc50d5f2b20179ecc284aee9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyregtab-0.7.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL pyregtab-0.7.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.4 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
2e52d68d7e941e7fbdc3e26311d70e6b93ad096ce134bda119a4a60fea5db24c
BLAKE2b-256 checksum
How to use checksums
ce7a069d4aaa83ce2a5ecd436fe15a38175cf8ad074cc97c14f885d960a0d943
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyregtab-0.7.1-cp310-abi3-macosx_11_0_arm64.whl

Download URL pyregtab-0.7.1-cp310-abi3-macosx_11_0_arm64.whl
Size 1.3 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
10fab663612f45f770c938e1fad59c2de61064048571f6a8a8dd0a23eef53195
BLAKE2b-256 checksum
How to use checksums
1dfde9a9b30cf58b246b928f336eef34f0e25512a17ed05d78d871388790c2db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release files / pyregtab-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl

Download URL pyregtab-0.7.1-cp310-abi3-macosx_10_12_x86_64.whl
Size 1.4 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
1426391adf5ca78c5ddf459070490376ee5d6134b4bc230c844b36c214a565c8
BLAKE2b-256 checksum
How to use checksums
e8c777885486f0a55209873d900b5b1728c23dcfebf980c76e736ec7541c644d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via maturin/1.15.0

Release history Release notifications | RSS feed

0.7.2

5 release files

This release

0.7.1 This release

5 release files

0.5.1

5 release files

0.5.0

5 release files

0.4.0

5 release files

0.3.0

5 release files

0.2.0

5 release files

0.1.1

5 release files

0.1.0

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page