Skip to content

Commit

Permalink
Merge pull request #258 from tomhea/feature/ci
Browse files Browse the repository at this point in the history
Feature/ci - tox with pytest, mypy, flake8 on all py versions and 3 platforms
  • Loading branch information
tomhea authored Dec 11, 2023
2 parents a5c2997 + b79b7cd commit 962612f
Show file tree
Hide file tree
Showing 27 changed files with 312 additions and 163 deletions.
49 changes: 49 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Tests

on: pull_request

env:
COLUMNS: 120

jobs:
test_py_os_variations:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
- name: Test with tox py${{ matrix.python-version }}-${{ matrix.os }}
run: tox -v

test_full:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: 3.12
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[tests]
- name: Test pytest
run: |
pytest --compile -n auto --all --junitxml=./compile-test-results.xml
pytest --run -n auto --all --junitxml=./run-test-results.xml
- name: Show tests results
if: always()
uses: pmeier/pytest-results-action@main
with:
path: ./*-test-results.xml
10 changes: 7 additions & 3 deletions flipjump/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from flipjump.flipjump_cli import assemble_run_according_to_cmd_line_args
from flipjump.flipjump_quickstart import (assemble, run, debug, run_test_output,
assemble_and_run, assemble_and_debug, assemble_and_run_test_output,
FJMVersion, TerminationCause, TerminationStatistics,
FixedIO, IODevice, StandardIO)
assemble_and_run, assemble_and_debug, assemble_and_run_test_output)
from flipjump.fjm.fjm_consts import FJMVersion
from flipjump.interpretter.fjm_run import TerminationStatistics
from flipjump.utils.classes import TerminationCause
from flipjump.utils.exceptions import IODeviceException, FlipJumpException
from flipjump.interpretter.io_devices.IODevice import IODevice
from flipjump.interpretter.io_devices.FixedIO import FixedIO
from flipjump.interpretter.io_devices.StandardIO import StandardIO
from flipjump.interpretter.io_devices.BrokenIO import BrokenIO


Expand Down
12 changes: 6 additions & 6 deletions flipjump/assembler/assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@
from flipjump.assembler.preprocessor import resolve_macros


def assert_address_in_memory(memory_width: int, address: int):
def assert_address_in_memory(memory_width: int, address: int) -> None:
if address < 0 or address >= (1 << memory_width):
raise FlipJumpAssemblerException(f"Not enough space with the {memory_width}-bits memory-width.")


def validate_addresses(memory_width, first_address, last_address):
def validate_addresses(memory_width: int, first_address: int, last_address: int) -> None:
if first_address % memory_width != 0 or last_address % memory_width != 0:
raise FlipJumpAssemblerException(f'segment boundaries are unaligned: '
f'[{hex(first_address)}, {hex(last_address - 1)}].')
Expand Down Expand Up @@ -81,7 +81,7 @@ def __init__(self, memory_width: int, first_segment: NewSegment, labels: Dict[st
self.padding_ops_indices: List[int] = [] # indices in self.fj_words

# return_address -> { (f3, f2, f1, f0) -> start_flip_address }
self.wflips_dict: Dict[int, Dict[Tuple[int, ...]]] = defaultdict(lambda: {})
self.wflips_dict: Dict[int, Dict[Tuple[int, ...], int]] = defaultdict(lambda: {})

def get_wflip_spot(self) -> WFlipSpot:
if self.padding_ops_indices:
Expand All @@ -101,7 +101,7 @@ def close_and_add_segment(self, fjm_writer: Writer) -> None:
self.first_address, self.next_wflip_address,
self.fj_words, self.wflip_words)

def _insert_wflip_label(self, address: int):
def _insert_wflip_label(self, address: int) -> None:
self.labels[f'{WFLIP_LABEL_PREFIX}{self.wflips_so_far}'] = address
self.wflips_so_far += 1

Expand Down Expand Up @@ -180,7 +180,7 @@ def labels_resolve(ops: Deque[LastPhaseOp], labels: Dict[str, int],
@param memory_width: the memory-width
@param fjm_writer: [out]: the .fjm file writer
"""
first_segment: NewSegment = ops.popleft()
first_segment = ops.popleft()
if not isinstance(first_segment, NewSegment):
raise FlipJumpAssemblerException(f"The first op must be of type NewSegment (and not {first_segment}).")

Expand Down Expand Up @@ -223,7 +223,7 @@ def assemble(input_files: List[Tuple[str, Path]],
debugging_file_path: Optional[Path] = None,
show_statistics: bool = False,
print_time: bool = True,
max_recursion_depth: Optional[int] = DEFAULT_MAX_MACRO_RECURSION_DEPTH,
max_recursion_depth: int = DEFAULT_MAX_MACRO_RECURSION_DEPTH,
) -> None:
"""
runs the assembly pipeline. assembles the input files to a .fjm.
Expand Down
19 changes: 12 additions & 7 deletions flipjump/assembler/fj_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,22 @@
from flipjump.utils.exceptions import FlipJumpExprException, FlipJumpParsingException
from flipjump.assembler.inner_classes.expr import Expr, get_minimized_expr
from flipjump.assembler.inner_classes.ops import get_used_labels, get_declared_labels, \
CodePosition, MacroName, Op, Macro, initial_macro_name, \
CodePosition, MacroName, Op, Macro, INITIAL_MACRO_NAME, \
MacroCall, RepCall, FlipJump, WordFlip, Label, Segment, Reserve, Pad

global curr_file, curr_file_short_name, curr_text, error_occurred, all_errors, curr_namespace
curr_file: Path
curr_file_short_name: str
curr_text: str
curr_namespace: List[str]
all_errors: str
error_occurred: bool


def get_position(lineno: int) -> CodePosition:
return CodePosition(curr_file, curr_file_short_name, lineno)
return CodePosition(str(curr_file), curr_file_short_name, lineno)


def syntax_error(lineno: int, msg='') -> None:
def syntax_error(lineno: int, msg: str = '') -> None:
global error_occurred, all_errors
error_occurred = True
curr_position = get_position(lineno)
Expand Down Expand Up @@ -217,7 +222,7 @@ def __init__(self, memory_width: int, warning_as_errors: bool, first_file: Tuple
self.consts: Dict[str, Expr] = {'w': Expr(memory_width)}
self.warning_as_errors: bool = warning_as_errors
self.macros: Dict[MacroName, Macro] = {
initial_macro_name: Macro([], [], [], '', _get_main_macro_code_position(first_file))
INITIAL_MACRO_NAME: Macro([], [], [], '', _get_main_macro_code_position(first_file))
}

def validate_free_macro_name(self, name: MacroName, lineno: int) -> None:
Expand Down Expand Up @@ -362,7 +367,7 @@ def error(self, token: Token) -> None:
@_('definable_line_statements')
def program(self, p: ParsedRule) -> None:
ops = p.definable_line_statements
self.macros[initial_macro_name].ops += ops
self.macros[INITIAL_MACRO_NAME].ops += ops

# noinspection PyUnresolvedReferences
@_('definable_line_statements NL definable_line_statement')
Expand Down Expand Up @@ -734,7 +739,7 @@ def parse_macro_tree(input_files: List[Tuple[str, Path]], memory_width: int, war
files_seen: Set[Union[str, Path]] = set()

if not input_files:
raise FlipJumpParsingException(f"The FlipJump parser got an empty files list.")
raise FlipJumpParsingException("The FlipJump parser got an empty files list.")

lexer = FJLexer()
parser = FJParser(memory_width, warning_as_errors, input_files[0])
Expand Down
13 changes: 8 additions & 5 deletions flipjump/assembler/inner_classes/expr.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from __future__ import annotations

from operator import mul, add, sub, floordiv, lshift, rshift, mod, xor, or_, and_
from typing import Union, Tuple, Set, Dict
from typing import Union, Tuple, Set, Dict, Callable

from flipjump.utils.exceptions import FlipJumpExprException


# dictionary from a math-op string, to its pythonic function.
# @note: if changed, update Expr.__str__().
op_string_to_function = {
UNARY_TYPE = Callable[[int], int]
BINARY_TYPE = Callable[[int, int], int]
TRINARY_TYPE = Callable[[int, int, int], int]
op_string_to_function: Dict[str, Union[UNARY_TYPE, BINARY_TYPE, TRINARY_TYPE]] = {
'+': add, '-': sub, '*': mul, '/': floordiv, '%': mod,
'<<': lshift, '>>': rshift, '^': xor, '|': or_, '&': and_,
'#': lambda x: x.bit_length(),
Expand All @@ -32,9 +35,9 @@ def __init__(self, expr: Union[int, str, Tuple[str, Tuple[Expr, ...]]]):
def is_int(self) -> bool:
return isinstance(self.value, int)

def __int__(self):
def __int__(self) -> int:
if self.is_int():
return self.value
return self.value # type: ignore[return-value]
raise FlipJumpExprException(f"Can't resolve labels: {', '.join(self.all_unknown_labels())}")

def all_unknown_labels(self) -> Set[str]:
Expand Down Expand Up @@ -67,7 +70,7 @@ def eval_new(self, params_dict: Dict[str, Expr]) -> Expr:
evaluated_args: Tuple[Expr, ...] = tuple(e.eval_new(params_dict) for e in args)
if all(isinstance(e.value, int) for e in evaluated_args):
try:
return Expr(op_string_to_function[op](*(arg.value for arg in evaluated_args)))
return Expr(op_string_to_function[op](*(arg.value for arg in evaluated_args))) # type: ignore[arg-type]
except Exception as e:
raise FlipJumpExprException(f'{repr(e)}. bad math operation ({op}): {str(self)}.')
return Expr((op, evaluated_args))
Expand Down
34 changes: 17 additions & 17 deletions flipjump/assembler/inner_classes/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import dataclasses
import os
from dataclasses import dataclass
from typing import Union, Dict, Set, List, Tuple
from typing import Union, Dict, Set, List, Tuple, Any

from flipjump.utils.exceptions import FlipJumpExprException
from flipjump.assembler.inner_classes.expr import Expr
Expand Down Expand Up @@ -41,23 +41,23 @@ def __str__(self) -> str:
return self.name
return f"{self.name}({self.parameter_num})"

def to_tuple(self):
def to_tuple(self) -> Tuple[str, int]:
return self.name, self.parameter_num

def __hash__(self):
def __hash__(self) -> int:
return hash(self.to_tuple())

def __eq__(self, other):
return type(other) == MacroName and self.to_tuple() == other.to_tuple()
def __eq__(self, other: Any) -> bool:
return isinstance(other, MacroName) and self.to_tuple() == other.to_tuple()

def __repr__(self):
def __repr__(self) -> str:
return str(self)


# The macro that holds the ops that are outside any macro.
initial_macro_name = MacroName('')
initial_args = []
initial_labels_prefix = ''
INITIAL_MACRO_NAME = MacroName('')
INITIAL_ARGS: List[Expr] = []
INITIAL_LABELS_PREFIX = ''


class FlipJump:
Expand All @@ -69,7 +69,7 @@ def __init__(self, flip: Expr, jump: Expr, code_position: CodePosition):
self.jump = jump
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"Flip: {self.flip}, Jump: {self.jump}, at {self.code_position}"

def eval_new(self, labels_dict: Dict[str, Expr]) -> FlipJump:
Expand Down Expand Up @@ -97,7 +97,7 @@ def __init__(self, word_address: Expr, flip_value: Expr, return_address: Expr, c
self.return_address = return_address
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"Flip Word {self.word_address} by {self.flip_value}, and return to {self.return_address}. " \
f"at {self.code_position}"

Expand Down Expand Up @@ -128,7 +128,7 @@ def __init__(self, ops_alignment: Expr, code_position: CodePosition):
self.ops_alignment = ops_alignment
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"Pad {self.ops_alignment} ops, at {self.code_position}"

def eval_new(self, labels_dict: Dict[str, Expr]) -> Pad:
Expand All @@ -152,7 +152,7 @@ def __init__(self, start_address: Expr, code_position: CodePosition):
self.start_address = start_address
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"Segment {self.start_address}, at {self.code_position}"

def eval_new(self, labels_dict: Dict[str, Expr]) -> Segment:
Expand All @@ -176,7 +176,7 @@ def __init__(self, reserved_bit_size: Expr, code_position: CodePosition):
self.reserved_bit_size = reserved_bit_size
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"Reserve {self.reserved_bit_size}, at {self.code_position}"

def eval_new(self, labels_dict: Dict[str, Expr]) -> Reserve:
Expand All @@ -201,7 +201,7 @@ def __init__(self, macro_name: str, arguments: List[Expr], code_position: CodePo
self.arguments = arguments
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"macro call. {self.macro_name.name} {', '.join(map(str, self.arguments))}. at {self.code_position}"

def eval_new(self, labels_dict: Dict[str, Expr]) -> MacroCall:
Expand All @@ -228,7 +228,7 @@ def __init__(self, repeat_times: Expr, iterator_name: str, macro_name: str, argu
self.arguments = arguments
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f"rep call. rep({self.repeat_times}, {self.iterator_name}) {self.macro_name.name} " \
f"{', '.join(map(str, self.arguments))}. at {self.code_position}"

Expand Down Expand Up @@ -279,7 +279,7 @@ def __init__(self, name: str, code_position: CodePosition):
self.name = name
self.code_position = code_position

def __str__(self):
def __str__(self) -> str:
return f'Label "{self.name}:", at {self.code_position}'

def eval_name(self, labels_dict: Dict[str, Expr]) -> str:
Expand Down
Loading

0 comments on commit 962612f

Please sign in to comment.