Merge pull request #64 from kbsriram/add-types

Add type annotations for library and tests.
This commit is contained in:
Scott Shawcroft 2024-05-29 11:19:05 -07:00 committed by GitHub
commit 73170ebf96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 48 additions and 40 deletions

View file

@ -11,6 +11,11 @@ Simple assembler to convert pioasm to bytes
* Author(s): Scott Shawcroft * Author(s): Scott Shawcroft
""" """
try:
from typing import List, MutableSequence
except ImportError:
pass
import array import array
import re import re
@ -40,14 +45,14 @@ class Program: # pylint: disable=too-few-public-methods
""" """
def __init__(self, text_program: str, *, build_debuginfo=False) -> None: def __init__(self, text_program: str, *, build_debuginfo: bool = False) -> None:
"""Converts pioasm text to encoded instruction bytes""" """Converts pioasm text to encoded instruction bytes"""
# pylint: disable=too-many-branches,too-many-statements,too-many-locals # pylint: disable=too-many-branches,too-many-statements,too-many-locals
assembled = [] assembled: List[int] = []
program_name = None program_name = None
labels = {} labels = {}
linemap = [] linemap = []
instructions = [] instructions: List[str] = []
sideset_count = 0 sideset_count = 0
sideset_enable = 0 sideset_enable = 0
wrap = None wrap = None
@ -86,9 +91,8 @@ class Program: # pylint: disable=too-few-public-methods
max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1 max_delay = 2 ** (5 - sideset_count - sideset_enable) - 1
assembled = [] assembled = []
for instruction in instructions: for line in instructions:
# print(instruction) instruction = splitter(line.strip())
instruction = splitter(instruction.strip())
delay = 0 delay = 0
if instruction[-1].endswith("]"): # Delay if instruction[-1].endswith("]"): # Delay
delay = int(instruction[-1].strip("[]"), 0) delay = int(instruction[-1].strip("[]"), 0)
@ -253,16 +257,13 @@ class Program: # pylint: disable=too-few-public-methods
self.assembled = array.array("H", assembled) self.assembled = array.array("H", assembled)
if build_debuginfo: self.debuginfo = (linemap, text_program) if build_debuginfo else None
self.debuginfo = (linemap, text_program)
else:
self.debuginfo = None
def print_c_program(self, name, qualifier="const"): def print_c_program(self, name: str, qualifier: str = "const") -> None:
"""Print the program into a C program snippet""" """Print the program into a C program snippet"""
if self.debuginfo is None: if self.debuginfo is None:
linemap = None linemap = []
program_lines = None program_lines = []
else: else:
linemap = self.debuginfo[0][:] # Use a copy since we destroy it linemap = self.debuginfo[0][:] # Use a copy since we destroy it
program_lines = self.debuginfo[1].split("\n") program_lines = self.debuginfo[1].split("\n")
@ -306,7 +307,7 @@ class Program: # pylint: disable=too-few-public-methods
print() print()
def assemble(program_text: str) -> array.array: def assemble(program_text: str) -> MutableSequence[int]:
"""Converts pioasm text to encoded instruction bytes """Converts pioasm text to encoded instruction bytes
In new code, prefer to use the `Program` class so that the extra arguments In new code, prefer to use the `Program` class so that the extra arguments

View file

@ -6,17 +6,22 @@
Pytest helper functions Pytest helper functions
""" """
try:
from typing import Any, List, Optional, Type
except ImportError:
pass
import pytest import pytest
import adafruit_pioasm import adafruit_pioasm
def nice_opcode(opcode): def nice_opcode(opcode: int) -> str:
opcode = f"{opcode:016b}" nice = f"{opcode:016b}"
return opcode[:3] + "_" + opcode[3:8] + "_" + opcode[8:] return nice[:3] + "_" + nice[3:8] + "_" + nice[8:]
def assert_assembles_to(source, expected): def assert_assembles_to(source: str, expected: List[int]) -> None:
actual = adafruit_pioasm.assemble(source) actual = adafruit_pioasm.assemble(source)
expected_bin = [nice_opcode(x) for x in expected] expected_bin = [nice_opcode(x) for x in expected]
actual_bin = [nice_opcode(x) for x in actual] actual_bin = [nice_opcode(x) for x in actual]
@ -25,7 +30,9 @@ def assert_assembles_to(source, expected):
), f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}" ), f"Assembling {source!r}: Expected {expected_bin}, got {actual_bin}"
def assert_assembly_fails(source, match=None, errtype=RuntimeError): def assert_assembly_fails(
source: str, match: Optional[str] = None, errtype: Type[Exception] = RuntimeError
) -> None:
with pytest.raises(errtype, match=match): with pytest.raises(errtype, match=match):
adafruit_pioasm.assemble(source) adafruit_pioasm.assemble(source)
# if match: # if match:
@ -36,6 +43,6 @@ def assert_assembly_fails(source, match=None, errtype=RuntimeError):
# adafruit_pioasm.assemble(source) # adafruit_pioasm.assemble(source)
def assert_pio_kwargs(source, **kw): def assert_pio_kwargs(source: str, **kw: Any) -> None:
program = adafruit_pioasm.Program(source) program = adafruit_pioasm.Program(source)
assert kw == program.pio_kwargs assert kw == program.pio_kwargs

View file

@ -57,13 +57,13 @@ def test_in_delay_with_sideset() -> None:
assert_assembles_to("\n".join(source), [0b010_10_101_000_10001]) assert_assembles_to("\n".join(source), [0b010_10_101_000_10001])
def test_in_bad_source(): def test_in_bad_source() -> None:
assert_assembly_fails( assert_assembly_fails(
"in bad, 17", match="Invalid in source 'bad'", errtype=ValueError "in bad, 17", match="Invalid in source 'bad'", errtype=ValueError
) )
def test_in_bad_bitcount(): def test_in_bad_bitcount() -> None:
assert_assembly_fails( assert_assembly_fails(
"in pins, 0", match="Count out of range", errtype=RuntimeError "in pins, 0", match="Count out of range", errtype=RuntimeError
) )

View file

@ -9,21 +9,21 @@ Tests mov
from pytest_helpers import assert_assembles_to, assert_assembly_fails from pytest_helpers import assert_assembles_to, assert_assembly_fails
def test_mov_non_happy(): def test_mov_non_happy() -> None:
# non happy path # non happy path
assert_assembly_fails( assert_assembly_fails(
"mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError "mov x, blah", match="Invalid mov source 'blah'", errtype=ValueError
) )
def test_mov_invert(): def test_mov_invert() -> None:
# test moving and inverting # test moving and inverting
assert_assembles_to("mov x, ~ x", [0b101_00000_001_01_001]) assert_assembles_to("mov x, ~ x", [0b101_00000_001_01_001])
assert_assembles_to("mov x, ~x", [0b101_00000_001_01_001]) assert_assembles_to("mov x, ~x", [0b101_00000_001_01_001])
assert_assembles_to("mov x, !x", [0b101_00000_001_01_001]) assert_assembles_to("mov x, !x", [0b101_00000_001_01_001])
def test_mov_reverse(): def test_mov_reverse() -> None:
# test moving and reversing bits # test moving and reversing bits
assert_assembles_to("mov x, :: x", [0b101_00000_001_10_001]) assert_assembles_to("mov x, :: x", [0b101_00000_001_10_001])
assert_assembles_to("mov x, ::x", [0b101_00000_001_10_001]) assert_assembles_to("mov x, ::x", [0b101_00000_001_10_001])

View file

@ -9,11 +9,11 @@ Tests nop
from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs from pytest_helpers import assert_assembles_to, assert_assembly_fails, assert_pio_kwargs
def test_nonsense(): def test_nonsense() -> None:
assert_assembly_fails("nope") assert_assembly_fails("nope")
def test_nop(): def test_nop() -> None:
assert_assembles_to("nop", [0b101_00000_010_00_010]) assert_assembles_to("nop", [0b101_00000_010_00_010])
assert_assembles_to("nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010]) assert_assembles_to("nop\nnop", [0b101_00000_010_00_010, 0b101_00000_010_00_010])
assert_assembles_to("nop [1]", [0b101_00001_010_00_010]) assert_assembles_to("nop [1]", [0b101_00001_010_00_010])
@ -22,7 +22,7 @@ def test_nop():
assert_assembles_to(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010]) assert_assembles_to(".side_set 1\nnop side 1 [15]", [0b101_11111_010_00_010])
def test_sideset_opt(): def test_sideset_opt() -> None:
assert_assembles_to(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 1", [0b101_11000_010_00_010])
assert_assembles_to(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 0", [0b101_10000_010_00_010])
assert_assembles_to(".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 0 [1]", [0b101_10001_010_00_010])
@ -32,14 +32,14 @@ def test_sideset_opt():
assert_assembles_to(".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010]) assert_assembles_to(".side_set 1 opt\nnop side 0 [7]", [0b101_10111_010_00_010])
def test_set(): def test_set() -> None:
# non happy path # non happy path
assert_assembly_fails( assert_assembly_fails(
"set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError "set isr, 1", match="Invalid set destination 'isr'", errtype=ValueError
) )
def test_jmp(): def test_jmp() -> None:
assert_assembles_to("l:\njmp l", [0b000_00000_000_00000]) assert_assembles_to("l:\njmp l", [0b000_00000_000_00000])
assert_assembles_to("l:\njmp 7", [0b000_00000_000_00111]) assert_assembles_to("l:\njmp 7", [0b000_00000_000_00111])
assert_assembles_to("jmp l\nl:", [0b000_00000_000_00001]) assert_assembles_to("jmp l\nl:", [0b000_00000_000_00001])
@ -56,7 +56,7 @@ def test_jmp():
) )
def test_wait(): def test_wait() -> None:
assert_assembles_to("wait 0 gpio 0", [0b001_00000_0_00_00000]) assert_assembles_to("wait 0 gpio 0", [0b001_00000_0_00_00000])
assert_assembles_to("wait 0 gpio 1", [0b001_00000_0_00_00001]) assert_assembles_to("wait 0 gpio 1", [0b001_00000_0_00_00001])
assert_assembles_to("wait 1 gpio 2", [0b001_00000_1_00_00010]) assert_assembles_to("wait 1 gpio 2", [0b001_00000_1_00_00010])
@ -69,7 +69,7 @@ def test_wait():
assert_assembles_to("wait 0 irq 1 rel", [0b001_00000_0_10_10001]) assert_assembles_to("wait 0 irq 1 rel", [0b001_00000_0_10_10001])
def test_limits(): def test_limits() -> None:
assert_assembly_fails(".side_set 1\nnop side 2") assert_assembly_fails(".side_set 1\nnop side 2")
assert_assembly_fails(".side_set 1\nnop side 2 [1]") assert_assembly_fails(".side_set 1\nnop side 2 [1]")
assert_assembly_fails("nop [32]") assert_assembly_fails("nop [32]")
@ -77,7 +77,7 @@ def test_limits():
assert_assembly_fails(".side_set 1 opt\nnop side 0 [8]") assert_assembly_fails(".side_set 1 opt\nnop side 0 [8]")
def test_cls(): def test_cls() -> None:
assert_pio_kwargs("", sideset_enable=False) assert_pio_kwargs("", sideset_enable=False)
assert_pio_kwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False) assert_pio_kwargs(".side_set 1", sideset_pin_count=1, sideset_enable=False)
assert_pio_kwargs(".side_set 3 opt", sideset_pin_count=3, sideset_enable=True) assert_pio_kwargs(".side_set 3 opt", sideset_pin_count=3, sideset_enable=True)

View file

@ -59,13 +59,13 @@ def test_out_delay_with_sideset() -> None:
assert_assembles_to("\n".join(source), [0b011_10_101_000_10001]) assert_assembles_to("\n".join(source), [0b011_10_101_000_10001])
def test_out_bad_destination(): def test_out_bad_destination() -> None:
assert_assembly_fails( assert_assembly_fails(
"out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError "out bad, 17", match="Invalid out destination 'bad'", errtype=ValueError
) )
def test_out_bad_bitcount(): def test_out_bad_bitcount() -> None:
assert_assembly_fails( assert_assembly_fails(
"out pins, 0", match="Count out of range", errtype=RuntimeError "out pins, 0", match="Count out of range", errtype=RuntimeError
) )

View file

@ -9,5 +9,5 @@ Tests pseudo-ops
from pytest_helpers import assert_pio_kwargs from pytest_helpers import assert_pio_kwargs
def test_offset(): def test_offset() -> None:
assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False) assert_pio_kwargs(".origin 7", offset=7, sideset_enable=False)

View file

@ -9,13 +9,13 @@ Tests radix
from pytest_helpers import assert_assembles_to from pytest_helpers import assert_assembles_to
def test_octal(): def test_octal() -> None:
assert_assembles_to(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001]) assert_assembles_to(".side_set 0o1\nset x, 0o11", [0b111_00000_001_01001])
def test_binary(): def test_binary() -> None:
assert_assembles_to(".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010]) assert_assembles_to(".side_set 0b101\nnop side 0b10001", [0b101_10001_010_00_010])
def test_hex(): def test_hex() -> None:
assert_assembles_to(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010]) assert_assembles_to(".side_set 0x0\nnop [0x10]", [0b101_10000_010_00_010])

View file

@ -9,7 +9,7 @@ Tests wrap
from pytest_helpers import assert_assembly_fails, assert_pio_kwargs from pytest_helpers import assert_assembly_fails, assert_pio_kwargs
def test_wrap(): def test_wrap() -> None:
assert_assembly_fails(".wrap") assert_assembly_fails(".wrap")
assert_pio_kwargs( assert_pio_kwargs(
"nop\n.wrap_target\nnop\nnop\n.wrap", "nop\n.wrap_target\nnop\nnop\n.wrap",