Skip to main content

Property-based testing utilities for finite automata and context-free grammars.

Project description

pbt4automata

Tests Python Version

pbt4automata provides tools to construct finite automata and context-free grammars in Python and validate their behavior with property-based tests powered by Hypothesis.

Installation

pip install -e .

Examples

Deterministic Finite Automata

Testing a DFA against a rule

The following example tests a DFA that accepts all strings that contain the substring "010" or "100".

from pbt4automata import DFA

automaton = DFA(
    states=["q0", "q1", "q2", "q3", "q4", "q5"],
    alphabet="01",
    transition_function={
        ("q0", "0"): "q1",
        ("q0", "1"): "q4",
        ("q1", "0"): "q1",
        ("q1", "1"): "q2",
        ("q2", "0"): "q3",
        ("q2", "1"): "q4",
        ("q3", "0"): "q3",
        ("q3", "1"): "q3",
        ("q4", "0"): "q5",
        ("q4", "1"): "q4",
        ("q5", "0"): "q3",
        ("q5", "1"): "q2",
    },
    start_state="q0",
    accept_states=["q3"],
)

result = automaton.test("[01]*(010|100)[01]*")

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to automaton.test(...) instead of a regex.

Testing two DFAs for equivalence

from pbt4automata import Automaton, DFA

automata1 = DFA(
    ...
)

automata2 = DFA(
    ...
)

result = Automaton.test_equivalence(automata1, automata2)

if result is True:
    print("Success!")
else:
    print("Counterexample:", repr(result))

Nondeterministic Finite Automata

Testing an NFA against a rule

NFAs support nondeterministic transitions (multiple possible next states for a given state and symbol) as well as epsilon (ε) transitions. The transition function maps (state, symbol) pairs to sets of states; use None as the symbol for ε-transitions. Unlike DFAs, the transition function may be partial.

The following example tests an NFA that accepts all strings over {0, 1} that end with the substring "01".

from pbt4automata import NFA

nfa = NFA(
    states=["q0", "q1", "q2"],
    alphabet="01",
    transition_function={
        ("q0", "0"): {"q0", "q1"},  # nondeterministically guess start of "01"
        ("q0", "1"): {"q0"},
        ("q1", "1"): {"q2"},
    },
    start_state="q0",
    accept_states=["q2"],
)

result = nfa.test("[01]*01")

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to nfa.test(...) instead of a regex.

NFA with epsilon transitions

from pbt4automata import NFA

# Accepts "a" or "ab"
nfa = NFA(
    states=["q0", "q1", "q2", "q3"],
    alphabet="ab",
    transition_function={
        ("q0", "a"): {"q1"},
        ("q1", None): {"q2"},   # ε-transition: q1 is also effectively q2
        ("q1", "b"): {"q3"},
    },
    start_state="q0",
    accept_states=["q2", "q3"],
)

Testing an NFA for equivalence with another automaton

Automaton.test_equivalence works with any mix of DFA and NFA objects sharing the same alphabet.

from pbt4automata import Automaton, DFA, NFA

nfa = NFA(...)
dfa = DFA(...)

result = Automaton.test_equivalence(nfa, dfa)

if result is True:
    print("Success!")
else:
    print("Counterexample:", repr(result))

Context-Free Grammars

Testing a CFG against a rule

The following example tests a CFG that generates all non-empty strings of balanced parentheses.

from pbt4automata import Grammar

grammar = Grammar(
    terminals="()",
    nonterminals="S",
    productions={
        "S": ["(S)", "SS", "()"],
    },
    start_symbol="S",
)

def check_balance(input_string: str) -> bool:
    if input_string == "":
        return False
    s = 0
    for c in input_string:
        if c == "(":
            s += 1
        elif c == ")":
            s -= 1
        if s < 0:
            return False
    return s == 0

result = grammar.test(check_balance)

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to grammar.test(...).

Parsing strings

from pbt4automata import Grammar

# Grammar for "a^n b^n" (n ≥ 1): S → aSb | ab
grammar = Grammar(
    terminals="ab",
    nonterminals="S",
    productions={
        "S": ["aSb", "ab"],
    },
    start_symbol="S",
)

print(grammar.parse("ab"))       # True
print(grammar.parse("aabb"))     # True
print(grammar.parse("aaabbb"))   # True
print(grammar.parse("aab"))      # False

Development

Install dev dependencies and run tests:

pip install -e ".[dev]"
pytest -v

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

pbt4automata-0.2.0.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

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

pbt4automata-0.2.0-py3-none-any.whl (9.0 kB view details)

Uploaded Python 3

File details

Details for the file pbt4automata-0.2.0.tar.gz.

File metadata

  • Download URL: pbt4automata-0.2.0.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pbt4automata-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4588e3e1e3ca74330f8389911f53864280d4b9258fa14f76fc2f22318ce90aeb
MD5 87c360da41e0fe1f49aa1d9c6468aff3
BLAKE2b-256 d87981224d9387c560aaf2a80db90470d213580f8210af23e69fd765aac13eb9

See more details on using hashes here.

Provenance

The following attestation bundles were made for pbt4automata-0.2.0.tar.gz:

Publisher: python-publish.yml on mrigankpawagi/pbt4automata

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

File details

Details for the file pbt4automata-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pbt4automata-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pbt4automata-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 46a395e62df220ad17fbc34d480963b44b1cd167bdfb9db6b49c29e9404971cd
MD5 bb0c42e49b5dc4fa9d99b0cd914deff4
BLAKE2b-256 c53eb42733a48355768050239fc9ff1f4d482af93682daa8fe239dfec0733778

See more details on using hashes here.

Provenance

The following attestation bundles were made for pbt4automata-0.2.0-py3-none-any.whl:

Publisher: python-publish.yml on mrigankpawagi/pbt4automata

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