Skip to content

Commit

Permalink
Better Regex Support and Error Propagation
Browse files Browse the repository at this point in the history
  • Loading branch information
donn committed Jun 9, 2024
1 parent f1c163e commit 4849956
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 17 deletions.
15 changes: 10 additions & 5 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
lib,
nix-gitignore,
buildPythonPackage,
pytest,
coverage,
}:

let
Expand All @@ -29,18 +31,21 @@ in buildPythonPackage rec {
version = builtins.head version_list;

src = nix-gitignore.gitignoreSourcePure ./.gitignore ./.;

doCheck = false;

PIP_DISABLE_PIP_VERSION_CHECK = "1";

nativeBuildInputs = [
antlr4_10
black
];

nativeCheckInputs = [
pytest
coverage
];

preBuild = ''
make antlr
'';
preBuild = "make antlr";
checkPhase = "pytest";

propagatedBuildInputs = [
antlr4_10-python3-runtime
Expand Down
18 changes: 16 additions & 2 deletions ioLexer.g
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,27 @@ Identifier: Nondigit (Nondigit | Digit)*;
At: '@';
Hash: '#';
Equal: '=';
Regex:
'^'? (
Digit
| Nondigit
| '.'
| '+'
| '*'
| '?'
| '\\'
| '['
| ']'
| '('
| ')'
| '|'
)+ '$'?;
Dollar: '$';
Regex: ( Digit | Nondigit | '.' | '*' | '\\' | '[' | ']')+;

// Common
fragment Cardinal: ('N' | 'E' | 'W' | 'S');
fragment Whitespace: (' ' | '\t' | EOL);
fragment EOL: ('\n');
fragment NonEOL: ~('\n');
fragment Nondigit: [a-zA-Z_];
fragment Digit: [0-9];
fragment Digit: [0-9];
2 changes: 1 addition & 1 deletion ioplace_parser/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
__version__ = "0.1.1"
__version__ = "0.2.0"

if __name__ == "__main__":
print(__version__, end="")
25 changes: 18 additions & 7 deletions ioplace_parser/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from enum import IntEnum
from typing import Optional, Dict, List, Union
from typing import Literal, Optional, Dict, List, Union
from decimal import Decimal
import warnings

Expand Down Expand Up @@ -45,7 +45,7 @@ class Side:


class myListener(ioListener):
sides: Dict[str, Side]
sides: Dict[Literal["N", "E", "W", "S"], Side]
current_side: Optional[Side] = None
global_sort_mode: Order = Order.busMajor
global_min_distance: Optional[Decimal] = None
Expand All @@ -71,7 +71,8 @@ def exitDirection(self, ctx: ioParser.DirectiveContext):
reverse_result=len(direction) == 2,
sort_mode=self.global_sort_mode,
)
self.sides[direction[0]] = self.current_side
side: Literal["N", "E", "W", "S"] = direction[0] # type: ignore
self.sides[side] = self.current_side

def exitRegex(self, ctx: ioParser.RegexContext):
if self.current_side is None:
Expand Down Expand Up @@ -121,13 +122,22 @@ def syntaxError(
raise ValueError(f"Syntax Error at {line}:{charPositionInLine}: {msg}")


def parse(string: str):
def parse(string: str) -> Dict[Literal["N", "E", "W", "S"], Side]:
"""
Parses a pin configuration into a dictionary of the four cardinal sides.
:param string: The input configuration as a string (not a file path)
:returns: A dictionary where each cardinal direction points to a Side object.
:raises ValueError: On syntax or token recognition errors
"""
listener = myListener()

stream = InputStream(string)

lexer = ioLexer(stream)
token_stream = CommonTokenStream(lexer)
lexer.addErrorListener(listener)

listener = myListener()
token_stream = CommonTokenStream(lexer)

parser = ioParser(token_stream)
parser.addErrorListener(listener)
Expand All @@ -137,7 +147,8 @@ def parse(string: str):
ParseTreeWalker.DEFAULT.walk(listener, tree)

sides_info = listener.sides
for side in ["N", "E", "W", "S"]:
sides: List[Literal["N", "E", "W", "S"]] = ["N", "E", "W", "S"]
for side in sides:
if side in sides_info:
continue
sides_info[side] = Side(
Expand Down
4 changes: 2 additions & 2 deletions test/example/complex.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ io_in\[16\]
io_out\[16\]
io_oeb\[16\]
analog_io\[10\]
io_in\[17\]
^io_in\[17\]$
io_out\[17\]
io_oeb\[17\]
analog_io\[11\]
Expand Down Expand Up @@ -162,4 +162,4 @@ io_oeb\[36\]
io_in\[37\]
io_out\[37\]
io_oeb\[37\]
$25
$25
101 changes: 101 additions & 0 deletions test/example/housekeeping.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#BUS_SORT
#W
debug_.*

trap.*

irq\[0\]
irq\[1\]
irq\[2\]
spi_sdoenb
spi_sdo
spi_sck
spi_csb
spi_sdi

ser_tx
ser_rx

qspi_enabled
uart_enabled
spi_enabled

wb_ack_o.*
wb_stb_i.*
wb_dat_o.*

spimemio.*

#E
serial_clock
serial_resetn
serial_load
serial_data_1
serial_data_2
mgmt_gpio_(in|out|oeb)\[0\]
mgmt_gpio_(in|out|oeb)\[1\]
mgmt_gpio_(in|out|oeb)\[2\]
mgmt_gpio_(in|out|oeb)\[3\]
mgmt_gpio_(in|out|oeb)\[4\]
mgmt_gpio_(in|out|oeb)\[5\]
mgmt_gpio_(in|out|oeb)\[6\]
mgmt_gpio_(in|out|oeb)\[7\]
mgmt_gpio_(in|out|oeb)\[8\]
mgmt_gpio_(in|out|oeb)\[9\]
mgmt_gpio_(in|out|oeb)\[10\]
mgmt_gpio_(in|out|oeb)\[11\]
mgmt_gpio_(in|out|oeb)\[12\]
mgmt_gpio_(in|out|oeb)\[13\]
mgmt_gpio_(in|out|oeb)\[14\]
mgmt_gpio_(in|out|oeb)\[15\]
mgmt_gpio_(in|out|oeb)\[16\]
mgmt_gpio_(in|out|oeb)\[17\]
mgmt_gpio_(in|out|oeb)\[18\]
mgmt_gpio_(in|out|oeb)\[19\]

#N
wb_adr_i.*
wb_dat_i.*
wb_sel_i.*

wb_we_i.*
wb_cyc_i.*
usr1_vcc_pwrgood
usr2_vcc_pwrgood
usr1_vdd_pwrgood
usr2_vdd_pwrgood
mgmt_gpio_(in|out|oeb)\[20\]
mgmt_gpio_(in|out|oeb)\[21\]
mgmt_gpio_(in|out|oeb)\[22\]
mgmt_gpio_(in|out|oeb)\[23\]
mgmt_gpio_(in|out|oeb)\[24\]
mgmt_gpio_(in|out|oeb)\[25\]
mgmt_gpio_(in|out|oeb)\[26\]
mgmt_gpio_(in|out|oeb)\[27\]
mgmt_gpio_(in|out|oeb)\[28\]
mgmt_gpio_(in|out|oeb)\[29\]
mgmt_gpio_(in|out|oeb)\[30\]
mgmt_gpio_(in|out|oeb)\[31\]
mgmt_gpio_(in|out|oeb)\[32\]
mgmt_gpio_(in|out|oeb)\[33\]
mgmt_gpio_(in|out|oeb)\[34\]
mgmt_gpio_(in|out|oeb)\[35\]
mgmt_gpio_(in|out|oeb)\[36\]
mgmt_gpio_(in|out|oeb)\[37\]

#S
user_clock
pad_flash_.*
porb
reset
pll_ena
pll_dco_ena
pll_div.*
pll_sel.*
pll90_sel.*
pll_trim.*
pll_bypass.*
wb_clk_i
wb_rstn_i
mask_rev_in.*
pwr_ctrl_out.*
128 changes: 128 additions & 0 deletions test/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,131 @@ def test_invalid_pin():
match=r"identifier/regex [^ ]+ requires a direction to be set first",
):
parse(example_str)


def test_housekeeping():
from ioplace_parser import parse, Side, Order

example_path = os.path.join(pytest.test_root, "example", "housekeeping.cfg")
example_str = open(example_path, encoding="utf8").read()

with pytest.warns(
UserWarning,
match=r"Specifying bit-major using the direction token \(\'\#BUS_SORT\'\) is deprecated",
):
assert parse(example_str) == {
"W": Side(
min_distance=None,
reverse_result=False,
pins=[
"debug_.*",
"trap.*",
"irq\\[0\\]",
"irq\\[1\\]",
"irq\\[2\\]",
"spi_sdoenb",
"spi_sdo",
"spi_sck",
"spi_csb",
"spi_sdi",
"ser_tx",
"ser_rx",
"qspi_enabled",
"uart_enabled",
"spi_enabled",
"wb_ack_o.*",
"wb_stb_i.*",
"wb_dat_o.*",
"spimemio.*",
],
sort_mode=Order.bitMajor,
),
"E": Side(
min_distance=None,
reverse_result=False,
pins=[
"serial_clock",
"serial_resetn",
"serial_load",
"serial_data_1",
"serial_data_2",
"mgmt_gpio_(in|out|oeb)\\[0\\]",
"mgmt_gpio_(in|out|oeb)\\[1\\]",
"mgmt_gpio_(in|out|oeb)\\[2\\]",
"mgmt_gpio_(in|out|oeb)\\[3\\]",
"mgmt_gpio_(in|out|oeb)\\[4\\]",
"mgmt_gpio_(in|out|oeb)\\[5\\]",
"mgmt_gpio_(in|out|oeb)\\[6\\]",
"mgmt_gpio_(in|out|oeb)\\[7\\]",
"mgmt_gpio_(in|out|oeb)\\[8\\]",
"mgmt_gpio_(in|out|oeb)\\[9\\]",
"mgmt_gpio_(in|out|oeb)\\[10\\]",
"mgmt_gpio_(in|out|oeb)\\[11\\]",
"mgmt_gpio_(in|out|oeb)\\[12\\]",
"mgmt_gpio_(in|out|oeb)\\[13\\]",
"mgmt_gpio_(in|out|oeb)\\[14\\]",
"mgmt_gpio_(in|out|oeb)\\[15\\]",
"mgmt_gpio_(in|out|oeb)\\[16\\]",
"mgmt_gpio_(in|out|oeb)\\[17\\]",
"mgmt_gpio_(in|out|oeb)\\[18\\]",
"mgmt_gpio_(in|out|oeb)\\[19\\]",
],
sort_mode=Order.bitMajor,
),
"N": Side(
min_distance=None,
reverse_result=False,
pins=[
"wb_adr_i.*",
"wb_dat_i.*",
"wb_sel_i.*",
"wb_we_i.*",
"wb_cyc_i.*",
"usr1_vcc_pwrgood",
"usr2_vcc_pwrgood",
"usr1_vdd_pwrgood",
"usr2_vdd_pwrgood",
"mgmt_gpio_(in|out|oeb)\\[20\\]",
"mgmt_gpio_(in|out|oeb)\\[21\\]",
"mgmt_gpio_(in|out|oeb)\\[22\\]",
"mgmt_gpio_(in|out|oeb)\\[23\\]",
"mgmt_gpio_(in|out|oeb)\\[24\\]",
"mgmt_gpio_(in|out|oeb)\\[25\\]",
"mgmt_gpio_(in|out|oeb)\\[26\\]",
"mgmt_gpio_(in|out|oeb)\\[27\\]",
"mgmt_gpio_(in|out|oeb)\\[28\\]",
"mgmt_gpio_(in|out|oeb)\\[29\\]",
"mgmt_gpio_(in|out|oeb)\\[30\\]",
"mgmt_gpio_(in|out|oeb)\\[31\\]",
"mgmt_gpio_(in|out|oeb)\\[32\\]",
"mgmt_gpio_(in|out|oeb)\\[33\\]",
"mgmt_gpio_(in|out|oeb)\\[34\\]",
"mgmt_gpio_(in|out|oeb)\\[35\\]",
"mgmt_gpio_(in|out|oeb)\\[36\\]",
"mgmt_gpio_(in|out|oeb)\\[37\\]",
],
sort_mode=Order.bitMajor,
),
"S": Side(
min_distance=None,
reverse_result=False,
pins=[
"user_clock",
"pad_flash_.*",
"porb",
"reset",
"pll_ena",
"pll_dco_ena",
"pll_div.*",
"pll_sel.*",
"pll90_sel.*",
"pll_trim.*",
"pll_bypass.*",
"wb_clk_i",
"wb_rstn_i",
"mask_rev_in.*",
"pwr_ctrl_out.*",
],
sort_mode=Order.bitMajor,
),
}, "Failed to properly parse housekeeping example"

0 comments on commit 4849956

Please sign in to comment.