Skip to content

Commit

Permalink
Merge e6b97c7 into 44503d7
Browse files Browse the repository at this point in the history
  • Loading branch information
Vipul-Cariappa committed Oct 13, 2023
2 parents 44503d7 + e6b97c7 commit c3eba5b
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 5 deletions.
14 changes: 14 additions & 0 deletions automata/pda/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""Classes and methods for working with PDA configurations."""

from dataclasses import dataclass
from typing import Any

from automata.base.automaton import AutomatonStateT
from automata.pda.stack import PDAStack
Expand All @@ -27,3 +28,16 @@ def __repr__(self) -> str:
return "{}({!r}, {!r}, {!r})".format(
self.__class__.__name__, self.state, self.remaining_input, self.stack
)

def __eq__(self, other: Any) -> bool:
"""Return True if two PDAConfiguration are equivalent"""
if not isinstance(other, PDAConfiguration):
raise NotImplemented

if (
self.state == other.state
and self.remaining_input == other.remaining_input
and self.stack == other.stack
):
return True
return False
38 changes: 37 additions & 1 deletion automata/pda/dpda.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/usr/bin/env python3
"""Classes and methods for working with deterministic pushdown automata."""

from typing import AbstractSet, Generator, Mapping, Optional, Set, Tuple, Union
from typing import AbstractSet, Generator, List, Mapping, Optional, Set, Tuple, Union

import automata.base.exceptions as exceptions
import automata.pda.exceptions as pda_exceptions
import automata.pda.pda as pda
from automata.base.utils import pairwise
from automata.pda.configuration import PDAConfiguration
from automata.pda.stack import PDAStack

Expand Down Expand Up @@ -53,6 +54,16 @@ def __init__(
acceptance_mode=acceptance_mode,
)

def iter_transitions(
self,
) -> Generator[Tuple[DPDAStateT, DPDAStateT, Tuple[str, str, str]], None, None]:
return (
(from_, to_, (input_symbol, stack_symbol, "".join(stack_push)))
for from_, input_lookup in self.transitions.items()
for input_symbol, stack_lookup in input_lookup.items()
for stack_symbol, (to_, stack_push) in stack_lookup.items()
)

def _validate_transition_invalid_symbols(
self, start_state: DPDAStateT, paths: DPDATransitionsT
) -> None:
Expand Down Expand Up @@ -151,6 +162,31 @@ def _get_next_configuration(self, old_config: PDAConfiguration) -> PDAConfigurat
)
return new_config

def _get_input_path(
self, input_str: str
) -> Tuple[List[Tuple[PDAConfiguration, PDAConfiguration]], bool]:
"""
Calculate the path taken by input.
Args:
input_str (str): The input string to run on the DPDA.
Returns:
tuple[list[tuple[PDAConfiguration, PDAConfiguration], bool]]: A list
of all transitions taken in each step and a boolean indicating
whether the DPDA accepted the input.
"""

state_history = list(self.read_input_stepwise(input_str))

path = list(pairwise(state_history))

last_state = state_history[-1] if state_history else self.initial_state
accepted = last_state in self.final_states

return path, accepted

def read_input_stepwise(
self, input_str: str
) -> Generator[PDAConfiguration, None, None]:
Expand Down
55 changes: 54 additions & 1 deletion automata/pda/npda.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#!/usr/bin/env python3
"""Classes and methods for working with nondeterministic pushdown automata."""

from typing import AbstractSet, Generator, Mapping, Set, Tuple, Union
from collections import deque
from typing import AbstractSet, Deque, Generator, List, Mapping, Set, Tuple, Union

import automata.base.exceptions as exceptions
import automata.pda.pda as pda
from automata.base.utils import pairwise
from automata.pda.configuration import PDAConfiguration
from automata.pda.stack import PDAStack

Expand All @@ -16,6 +18,8 @@
]
NPDATransitionsT = Mapping[NPDAStateT, NPDAPathT]

InputPathListT = List[Tuple[PDAConfiguration, PDAConfiguration]]


class NPDA(pda.PDA):
"""A nondeterministic pushdown automaton."""
Expand Down Expand Up @@ -55,6 +59,17 @@ def __init__(
acceptance_mode=acceptance_mode,
)

def iter_transitions(
self,
) -> Generator[Tuple[NPDAStateT, NPDAStateT, Tuple[str, str, str]], None, None]:
return (
(from_, to_, (input_symbol, stack_symbol, "".join(stack_push)))
for from_, input_lookup in self.transitions.items()
for input_symbol, stack_lookup in input_lookup.items()
for stack_symbol, op_ in stack_lookup.items()
for (to_, stack_push) in op_
)

def _validate_transition_invalid_symbols(
self, start_state: NPDAStateT, paths: NPDATransitionsT
) -> None:
Expand Down Expand Up @@ -111,6 +126,44 @@ def _get_next_configurations(
new_configs.add(new_config)
return new_configs

def _get_input_path(
self, input_str: str
) -> Tuple[List[Tuple[PDAConfiguration, PDAConfiguration]], bool]:
"""
Calculate the path taken by input.
Args:
input_str (str): The input string to run on the NPDA.
Returns:
tuple[list[tuple[PDAConfiguration, PDAConfiguration], bool]]: A list
of all transitions taken in each step and a boolean indicating
whether the NPDA accepted the input.
"""

steps = list(self.read_input_stepwise(input_str))

path: List[PDAConfiguration] = [steps.pop().pop()]

accepted = path[0] in self.final_states

for i in range(len(steps) - 1, -1, -1):
if len(steps[i]) == 1:
path.append(steps[i].pop())
continue

for curr_step in steps[i]:
for next_step in self._get_next_configurations(curr_step):
if next_step == path[-1]:
path.append(curr_step)
break
else:
continue
break

return list(pairwise(reversed(path))), accepted

def read_input_stepwise(
self, input_str: str
) -> Generator[Set[PDAConfiguration], None, None]:
Expand Down
Loading

0 comments on commit c3eba5b

Please sign in to comment.