From 01f5cb5b39dfc38134611a8509607d7765fd57a1 Mon Sep 17 00:00:00 2001 From: David Kopec Date: Tue, 4 Jun 2024 02:21:07 -0400 Subject: [PATCH] making some lines a bit more compact --- NanoBASIC/__main__.py | 2 +- NanoBASIC/interpreter.py | 27 ++++++++++++++---------- NanoBASIC/parser.py | 40 +++++++++++++++++++----------------- NanoBASIC/tokenizer.py | 6 ++++-- RetroDither/macpaint.py | 20 ++++++++++-------- StainedGlass/__main__.py | 21 ++++++++++--------- StainedGlass/stainedglass.py | 17 +++++++-------- StainedGlass/svg.py | 8 ++++---- tests/test_retrodither.py | 8 ++++---- 9 files changed, 81 insertions(+), 68 deletions(-) diff --git a/NanoBASIC/__main__.py b/NanoBASIC/__main__.py index 97439cc..0460453 100644 --- a/NanoBASIC/__main__.py +++ b/NanoBASIC/__main__.py @@ -19,6 +19,6 @@ if __name__ == "__main__": # Parse the file argument file_parser = ArgumentParser("NanoBASIC") - file_parser.add_argument("basic_file", help="A text file containing NanoBASIC source code.") + file_parser.add_argument("basic_file", help="A text file containing NanoBASIC code.") arguments = file_parser.parse_args() execute(arguments.basic_file) diff --git a/NanoBASIC/interpreter.py b/NanoBASIC/interpreter.py index 64ca95f..f99ae04 100644 --- a/NanoBASIC/interpreter.py +++ b/NanoBASIC/interpreter.py @@ -74,17 +74,17 @@ def interpret(self, statement: Statement): if (line_index := self.find_line_index(go_to_line_id)) is not None: self.statement_index = line_index else: - raise Interpreter.InterpreterError("Couldn't find GOTO line id.", self.current) + raise Interpreter.InterpreterError("No GOTO line id.", self.current) case GoSubStatement(line_expr=line_expr): go_sub_line_id = self.evaluate_numeric(line_expr) if (line_index := self.find_line_index(go_sub_line_id)) is not None: self.subroutine_stack.append(self.statement_index + 1) # Setup for RETURN self.statement_index = line_index else: - raise Interpreter.InterpreterError("Couldn't find GOSUB line id.", self.current) + raise Interpreter.InterpreterError("No GOSUB line id.", self.current) case ReturnStatement(): if not self.subroutine_stack: # Check if the stack is empty - raise Interpreter.InterpreterError("Return with no prior corresponding GOSUB.", self.current) + raise Interpreter.InterpreterError("RETURN without GOSUB.", self.current) self.statement_index = self.subroutine_stack.pop() case PrintStatement(printables=printables): accumulated_string: str = "" @@ -93,7 +93,7 @@ def interpret(self, statement: Statement): accumulated_string += "\t" if isinstance(printable, NumericExpression): accumulated_string += str(self.evaluate_numeric(printable)) - else: # Otherwise, it's a string, because that's the only other thing we allow + else: # Otherwise, it's a string accumulated_string += str(printable) print(accumulated_string) self.statement_index += 1 @@ -103,7 +103,8 @@ def interpret(self, statement: Statement): else: self.statement_index += 1 case _: - raise Interpreter.InterpreterError(f"Unexpected item {self.current} in statement list.", self.current) + raise Interpreter.InterpreterError(f"Unexpected item {self.current} " + f"in statement list.", self.current) def evaluate_numeric(self, numeric_expression: NumericExpression) -> int: match numeric_expression: @@ -113,12 +114,14 @@ def evaluate_numeric(self, numeric_expression: NumericExpression) -> int: if name in self.variable_table: return self.variable_table[name] else: - raise Interpreter.InterpreterError(f"Var {name} used before initialized.", numeric_expression) + raise Interpreter.InterpreterError(f"Var {name} used " + f"before initialized.", numeric_expression) case UnaryOperation(operator=operator, expr=expr): if operator is TokenType.MINUS: return -self.evaluate_numeric(expr) else: - raise Interpreter.InterpreterError(f"Expected - but got {operator}.", numeric_expression) + raise Interpreter.InterpreterError(f"Expected - " + f"but got {operator}.", numeric_expression) case BinaryOperation(operator=operator, left_expr=left, right_expr=right): if operator is TokenType.PLUS: return self.evaluate_numeric(left) + self.evaluate_numeric(right) @@ -129,9 +132,11 @@ def evaluate_numeric(self, numeric_expression: NumericExpression) -> int: elif operator is TokenType.DIVIDE: return self.evaluate_numeric(left) // self.evaluate_numeric(right) else: - raise Interpreter.InterpreterError(f"Unexpected binary operator {operator}.", numeric_expression) + raise Interpreter.InterpreterError(f"Unexpected binary operator " + f"{operator}.", numeric_expression) case _: - raise Interpreter.InterpreterError("Expected numeric expression.", numeric_expression) + raise Interpreter.InterpreterError("Expected numeric expression.", + numeric_expression) def evaluate_boolean(self, boolean_expression: BooleanExpression) -> bool: left = self.evaluate_numeric(boolean_expression.left_expr) @@ -150,5 +155,5 @@ def evaluate_boolean(self, boolean_expression: BooleanExpression) -> bool: case TokenType.NOT_EQUAL: return left != right case _: - raise Interpreter.InterpreterError(f"Unexpected boolean operator {boolean_expression.operator}.", - boolean_expression) + raise Interpreter.InterpreterError(f"Unexpected boolean operator " + f"{boolean_expression.operator}.", boolean_expression) diff --git a/NanoBASIC/parser.py b/NanoBASIC/parser.py index 81a7528..80c5d55 100644 --- a/NanoBASIC/parser.py +++ b/NanoBASIC/parser.py @@ -52,7 +52,7 @@ def out_of_tokens(self) -> bool: @property def current(self) -> Token: if self.out_of_tokens: - raise (Parser.ParserError(f"Ran out of tokens after {self.previous.kind}.", self.previous)) + raise (Parser.ParserError(f"No tokens after {self.previous.kind}.", self.previous)) return self.tokens[self.token_index] @property @@ -107,8 +107,10 @@ def parse_print(self, line_id: int) -> PrintStatement: printables.append(expression) last_col = expression.col_end else: - raise Parser.ParserError("Only strings and numeric expressions allowed in print list.", self.current) - if not self.out_of_tokens and self.current.kind is TokenType.COMMA: # Comma means there's more + raise Parser.ParserError("Only strings and numeric expressions " + "allowed in print list.", self.current) + # Comma means there's more to print + if not self.out_of_tokens and self.current.kind is TokenType.COMMA: self.consume(TokenType.COMMA) continue break @@ -168,8 +170,8 @@ def parse_boolean_expression(self) -> BooleanExpression: return BooleanExpression(line_num=left.line_num, col_start=left.col_start, col_end=right.col_end, operator=operator.kind, left_expr=left, right_expr=right) - raise Parser.ParserError(f"Expected boolean operator inside boolean expression but found {self.current.kind}.", - self.current) + raise Parser.ParserError(f"Expected boolean operator but found " + f"{self.current.kind}.", self.current) def parse_numeric_expression(self) -> NumericExpression: left = self.parse_term() @@ -180,15 +182,15 @@ def parse_numeric_expression(self) -> NumericExpression: if self.current.kind is TokenType.PLUS: self.consume(TokenType.PLUS) right = self.parse_term() - left = BinaryOperation(line_num=left.line_num, - col_start=left.col_start, col_end=right.col_end, - operator=TokenType.PLUS, left_expr=left, right_expr=right) + left = BinaryOperation(line_num=left.line_num, col_start=left.col_start, + col_end=right.col_end, operator=TokenType.PLUS, + left_expr=left, right_expr=right) elif self.current.kind is TokenType.MINUS: self.consume(TokenType.MINUS) right = self.parse_term() - left = BinaryOperation(line_num=left.line_num, - col_start=left.col_start, col_end=right.col_end, - operator=TokenType.MINUS, left_expr=left, right_expr=right) + left = BinaryOperation(line_num=left.line_num, col_start=left.col_start, + col_end=right.col_end, operator=TokenType.MINUS, + left_expr=left, right_expr=right) else: break # No more, must be end of expression return left @@ -202,15 +204,15 @@ def parse_term(self) -> NumericExpression: if self.current.kind is TokenType.MULTIPLY: self.consume(TokenType.MULTIPLY) right = self.parse_factor() - left = BinaryOperation(line_num=left.line_num, - col_start=left.col_start, col_end=right.col_end, - operator=TokenType.MULTIPLY, left_expr=left, right_expr=right) + left = BinaryOperation(line_num=left.line_num, col_start=left.col_start, + col_end=right.col_end, operator=TokenType.MULTIPLY, + left_expr=left, right_expr=right) elif self.current.kind is TokenType.DIVIDE: self.consume(TokenType.DIVIDE) right = self.parse_factor() - left = BinaryOperation(line_num=left.line_num, - col_start=left.col_start, col_end=right.col_end, - operator=TokenType.DIVIDE, left_expr=left, right_expr=right) + left = BinaryOperation(line_num=left.line_num, col_start=left.col_start, + col_end=right.col_end, operator=TokenType.DIVIDE, + left_expr=left, right_expr=right) else: break # No more, must be end of expression return left @@ -230,7 +232,7 @@ def parse_factor(self) -> NumericExpression: self.consume(TokenType.OPEN_PAREN) expression = self.parse_numeric_expression() if self.current.kind is not TokenType.CLOSE_PAREN: - raise Parser.ParserError("Expected matching closing parenthesis.", self.current) + raise Parser.ParserError("Expected matching close parenthesis.", self.current) self.consume(TokenType.CLOSE_PAREN) return expression elif self.current.kind is TokenType.MINUS: @@ -239,4 +241,4 @@ def parse_factor(self) -> NumericExpression: return UnaryOperation(line_num=minus.line_num, col_start=minus.col_start, col_end=expression.col_end, operator=TokenType.MINUS, expr=expression) - raise Parser.ParserError("Couldn't parse numeric expression, unexpected token.", self.current) + raise Parser.ParserError("Unexpected token in numeric expression.", self.current) diff --git a/NanoBASIC/tokenizer.py b/NanoBASIC/tokenizer.py index 09b143b..c281e55 100644 --- a/NanoBASIC/tokenizer.py +++ b/NanoBASIC/tokenizer.py @@ -84,7 +84,8 @@ def tokenize(text_file: TextIO) -> list[Token]: if found: col_end: int = col_start + found.end() - 1 # Store tokens other than comments and whitespace - if possibility is not TokenType.WHITESPACE and possibility is not TokenType.COMMENT: + if (possibility is not TokenType.WHITESPACE + and possibility is not TokenType.COMMENT): associated_value: str | int | None = None if possibility.has_associated_value: if possibility is TokenType.NUMBER: @@ -93,7 +94,8 @@ def tokenize(text_file: TextIO) -> list[Token]: associated_value = found.group() elif possibility is TokenType.STRING: # Remove quote characters associated_value = found.group(0)[1:-1] - tokens.append(Token(possibility, line_num, col_start, col_end, associated_value)) + tokens.append(Token(possibility, line_num, col_start, col_end, + associated_value)) # Continue search from place in line after token line = line[found.end():] col_start = col_end + 1 diff --git a/RetroDither/macpaint.py b/RetroDither/macpaint.py index b3fc190..7f6036d 100644 --- a/RetroDither/macpaint.py +++ b/RetroDither/macpaint.py @@ -25,7 +25,7 @@ # Convert an array of bytes where each byte is 0 or 255 # to an array of bits where each byte that is 0 becomes a 1 -# and each bit that is 255 becomes a 0 +# and each byte that is 255 becomes a 0 def bytes_to_bits(original: array) -> array: bits_array = array('B') @@ -65,7 +65,8 @@ def run_length_encode(original_data: array) -> array: # Find how many of the same bytes are in a row from *start* def take_same(source: array, start: int) -> int: count = 0 - while start + count + 1 < len(source) and source[start + count] == source[start + count + 1]: + while (start + count + 1 < len(source) + and source[start + count] == source[start + count + 1]): count += 1 return count + 1 if count > 0 else 0 @@ -76,7 +77,8 @@ def take_same(source: array, start: int) -> int: index = 0 while index < len(data): not_same = 0 - while ((same := take_same(data, index + not_same)) == 0) and (index + not_same < len(data)): + while (((same := take_same(data, index + not_same)) == 0) + and (index + not_same < len(data))): not_same += 1 if not_same > 0: rle_data.append(not_same - 1) @@ -92,13 +94,13 @@ def take_same(source: array, start: int) -> int: def macbinary_header(outfile: str, data_size: int) -> array: macbinary = array('B', [0] * MACBINARY_LENGTH) filename = Path(outfile).stem - filename = filename[:63] if len(filename) > 63 else filename # limit to 63 characters maximum + filename = filename[:63] if len(filename) > 63 else filename # limit to 63 characters max macbinary[1] = len(filename) # file name length macbinary[2:(2 + len(filename))] = array("B", filename.encode("mac_roman")) # file name macbinary[65:69] = array("B", "PNTG".encode("mac_roman")) # file type macbinary[69:73] = array("B", "MPNT".encode("mac_roman")) # file creator macbinary[83:87] = array("B", data_size.to_bytes(4, byteorder='big')) # size of data fork - timestamp = int((datetime.now() - datetime(1904, 1, 1)).total_seconds()) # Mac timestamps are seconds since 1904 + timestamp = int((datetime.now() - datetime(1904, 1, 1)).total_seconds()) # Mac timestamp macbinary[91:95] = array("B", timestamp.to_bytes(4, byteorder='big')) # creation stamp macbinary[95:99] = array("B", timestamp.to_bytes(4, byteorder='big')) # modification stamp return macbinary @@ -109,15 +111,15 @@ def write_macpaint_file(data: array, out_file: str, width: int, height: int): bits_array = prepare(data, width, height) rle = run_length_encode(bits_array) data_size = len(rle) + HEADER_LENGTH # header requires this - output_array = macbinary_header(out_file, data_size) + array('B', [0] * HEADER_LENGTH) + rle - output_array[MACBINARY_LENGTH + 3] = 2 # Data Fork Header Signature + output = macbinary_header(out_file, data_size) + array('B', [0] * HEADER_LENGTH) + rle + output[MACBINARY_LENGTH + 3] = 2 # Data Fork Header Signature # macbinary format requires that there be padding of 0s up to a # multiple of 128 bytes for the data fork padding = 128 - (data_size % 128) if padding > 0: - output_array += array('B', [0] * padding) + output += array('B', [0] * padding) with open(out_file + ".bin", "wb") as fp: - output_array.tofile(fp) + output.tofile(fp) # Alternative version of run_length_encode that doesn't use the helper function # # https://en.wikipedia.org/wiki/PackBits diff --git a/StainedGlass/__main__.py b/StainedGlass/__main__.py index f0cfdaf..05edbd7 100644 --- a/StainedGlass/__main__.py +++ b/StainedGlass/__main__.py @@ -19,24 +19,25 @@ if __name__ == "__main__": # Parse the file argument argument_parser = ArgumentParser("StainedGlass") - argument_parser.add_argument("image_file", help="An image file to paint a stained glass equivalent of.") + argument_parser.add_argument("image_file", help="The input image to be painted.") argument_parser.add_argument("output_file", help="The final resulting abstract art image.") argument_parser.add_argument('-t', '--trials', type=int, default=10000, help='The number of trials to run (default 10000).') - argument_parser.add_argument('-m', '--method', choices=['random', 'average', 'common'], default='average', - help='The method for determining shape colors (default average).') - argument_parser.add_argument('-s', '--shape', choices=['ellipse', 'triangle', 'quadrilateral', 'line'], - default='ellipse', + argument_parser.add_argument('-m', '--method', choices=['random', 'average', 'common'], + default='average', + help='Shape color determination method (default average).') + argument_parser.add_argument('-s', '--shape', choices=['ellipse', 'triangle', + 'quadrilateral', 'line'], default='ellipse', help='The shape type to use (default ellipse).') argument_parser.add_argument('-l', '--length', type=int, default=256, - help='The length (height) of the final image in pixels (default 256).') + help='The length of the final image in pixels (default 256).') argument_parser.add_argument('-v', '--vector', default=False, action='store_true', help='Create vector output. A SVG file will also be output.') argument_parser.add_argument('-a', '--animate', type=int, default=0, - help='If a number greater than 0 is provided, will create an animated ' - 'GIF with the number of milliseconds per frame provided.') + help='If greater than 0, will create an animated GIF ' + 'with the number of milliseconds per frame provided.') arguments = argument_parser.parse_args() method = ColorMethod[arguments.method.upper()] shape_type = ShapeType[arguments.shape.upper()] - StainedGlass(arguments.image_file, arguments.output_file, arguments.trials, method, shape_type, arguments.length, - arguments.vector, arguments.animate) + StainedGlass(arguments.image_file, arguments.output_file, arguments.trials, method, + shape_type, arguments.length, arguments.vector, arguments.animate) diff --git a/StainedGlass/stainedglass.py b/StainedGlass/stainedglass.py index 447e6b1..976d623 100644 --- a/StainedGlass/stainedglass.py +++ b/StainedGlass/stainedglass.py @@ -33,8 +33,8 @@ def get_most_common_color(image: Image.Image) -> tuple[int, int, int]: class StainedGlass: - def __init__(self, file_name: str, output_file: str, trials: int, method: ColorMethod, shape_type: ShapeType, - length: int, vector: bool, animation_length: int): + def __init__(self, file_name: str, output_file: str, trials: int, method: ColorMethod, + shape_type: ShapeType, length: int, vector: bool, animation_length: int): self.method = method self.shape_type = shape_type self.shapes = [] @@ -62,7 +62,7 @@ def __init__(self, file_name: str, output_file: str, trials: int, method: ColorM last_percent = percent print(f"{percent}% Done, Best Difference {self.best_difference}") end = timer() - print(f"{end-start} seconds elapsed. {len(self.shapes)} shapes created. Outputting image...") + print(f"{end-start} seconds elapsed. {len(self.shapes)} shapes created.") self.create_output(output_file, length, vector, animation_length) def difference(self, other_image: Image.Image) -> float: @@ -136,7 +136,7 @@ def experiment() -> bool: break self.shapes.append((coordinates, color)) - def create_output(self, output_file: str, height: int, vector: bool, animation_length: int): + def create_output(self, out_file: str, height: int, vector: bool, animation_length: int): average_color = tuple((round(n) for n in ImageStat.Stat(self.original).mean)) original_width, original_height = self.original.size ratio = height / original_height @@ -160,9 +160,10 @@ def create_output(self, output_file: str, height: int, vector: bool, animation_l svg.draw_polygon(coordinates, color) if animation_frames is not None: animation_frames.append(output_image.copy()) - output_image.save(output_file) + output_image.save(out_file) if svg: - svg.write(output_file + ".svg") + svg.write(out_file + ".svg") if animation_frames is not None: - animation_frames[0].save(output_file + ".gif", save_all=True, append_images=animation_frames[1:], - optimize=False, duration=animation_length, loop=0, transparency=0, disposal=2) + animation_frames[0].save(out_file + ".gif", save_all=True, + append_images=animation_frames[1:], optimize=False, + duration=animation_length, loop=0, transparency=0, disposal=2) diff --git a/StainedGlass/svg.py b/StainedGlass/svg.py index 54626dd..540b7f0 100644 --- a/StainedGlass/svg.py +++ b/StainedGlass/svg.py @@ -17,9 +17,9 @@ class SVG: def __init__(self, width: int, height: int, background_color: tuple[int, int, int]): self.content = '\n' \ - f'\n'\ - f'' + f'\n' \ + f'' def draw_ellipse(self, x1: int, y1: int, x2: int, y2: int, color: tuple[int, int, int]): self.content += f'\n' + 'stroke-width="1px" shape-rendering="crispEdges" />\n' def draw_polygon(self, coordinates: list[int], color: tuple[int, int, int]): points = "" diff --git a/tests/test_retrodither.py b/tests/test_retrodither.py index 0a41eb1..46b01bf 100644 --- a/tests/test_retrodither.py +++ b/tests/test_retrodither.py @@ -24,11 +24,11 @@ class RetroDitherTestCase(unittest.TestCase): # Example from https://web.archive.org/web/20080705155158/http://developer.apple.com/technotes/tn/tn1023.html def test_apple_rle_example(self): - unpacked = array("B", [0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0x80, 0x00, - 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]) + unpacked = array("B", [0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, + 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]) packed = run_length_encode(unpacked) - expected = array("B", [0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, 0x00, 0x2A, - 0x22, 0xF7, 0xAA]) + expected = array("B", [0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA, 0x03, 0x80, + 0x00, 0x2A, 0x22, 0xF7, 0xAA]) self.assertEqual(expected, packed) # Example where packed data is longer than unpacked data