Compare commits
	
		
			4 Commits
		
	
	
		
			752cf011c3
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cce7e96779 | |||
| 30339f0ece | |||
| b6e9c6f4db | |||
| d34ce4efd2 | 
							
								
								
									
										181
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| <h3>rivet - Register / Instruction Visualizer and Explainer Tool</h3> | ||||
|  | ||||
| --- | ||||
| ## Table of contents | ||||
| <!-- TOC --> | ||||
|   * [Table of contents](#table-of-contents) | ||||
|   * [Introduction](#introduction) | ||||
|   * [Requirements](#requirements) | ||||
|   * [Usage](#usage) | ||||
|   * [Options](#options) | ||||
|   * [Examples](#examples) | ||||
|       * [config.json](#configjson) | ||||
|       * [dark.json](#darkjson) | ||||
|       * [blueprint.json](#blueprintjson) | ||||
|       * [transparent.json](#transparentjson) | ||||
|   * [Config](#config) | ||||
| <!-- TOC --> | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Introduction | ||||
|  | ||||
| This tool lets you generate visual explanations of binary instructions, or describe the contents of a register. | ||||
|  | ||||
| The layout and style can be customized to fit your needs. | ||||
|  | ||||
| For a full description of the schema format, please check out [format.md](format.md) | ||||
|  | ||||
| ## Requirements | ||||
|  | ||||
| - [Python 3+](https://www.python.org/) | ||||
| - [Pygame](https://pypi.org/project/pygame/) - used to render the images | ||||
| - [Beautiful Soup 4](https://pypi.org/project/beautifulsoup4/) - used to parse XML files | ||||
| - [PyYAML](https://pypi.org/project/PyYAML/) - used to parse YAML files | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| Basic usage: | ||||
| ```bash | ||||
| python3 main.py schema.xml | ||||
| ``` | ||||
|  | ||||
| Directory mode + config: | ||||
| ```bash | ||||
| python3 main.py -o out/ -c config.json -d schemas/ | ||||
| ``` | ||||
|  | ||||
| ## Options | ||||
|  | ||||
| Several command line options are available: | ||||
| - `-o PATH` sets the output path. If not given, the output file will be located in the same directory as the input file, and given the same name (different extension) | ||||
| - `-d` enables directory mode. If set, the input and output paths are directories and all files inside the input directory are processed | ||||
| - `-c PATH` sets the config path. For more details on available config parameters, please read the [Config section](#config). | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| The following images were generated from this schema ([example1.yaml](example1.yaml)): | ||||
|  | ||||
| <details> | ||||
| <summary>Show / hide</summary> | ||||
|  | ||||
| ```yaml | ||||
| structures: | ||||
|   main: | ||||
|     bits: 32 | ||||
|     ranges: | ||||
|       31-28: | ||||
|         name: cond | ||||
|       27: | ||||
|         name: 0 | ||||
|       26: | ||||
|         name: 1 | ||||
|       25: | ||||
|         name: I | ||||
|       24: | ||||
|         name: P | ||||
|         description: pre / post indexing bit | ||||
|         values: | ||||
|           0: post, add offset after transfer | ||||
|           1: pre, add offset before transfer | ||||
|       23: | ||||
|         name: U | ||||
|         description: up / down bit | ||||
|         values: | ||||
|           0: down, subtract offset from base | ||||
|           1: up, addition offset to base | ||||
|       22: | ||||
|         name: B | ||||
|         description: byte / word bit | ||||
|         values: | ||||
|           0: transfer word quantity | ||||
|           1: transfer byte quantity | ||||
|       21: | ||||
|         name: W | ||||
|         description: write-back bit | ||||
|         values: | ||||
|           0: no write-back | ||||
|           1: write address into base | ||||
|       20: | ||||
|         name: L | ||||
|         description: load / store bit | ||||
|         values: | ||||
|           0: store to memory | ||||
|           1: load from memory | ||||
|       19-16: | ||||
|         name: Rn | ||||
|         description: base register | ||||
|       15-12: | ||||
|         name: Rd | ||||
|         description: source / destination register | ||||
|       11-0: | ||||
|         name: offset | ||||
|         depends-on: 25 | ||||
|         values: | ||||
|           0: | ||||
|             description: offset is an immediate value | ||||
|             structure: immediateOffset | ||||
|           1: | ||||
|             description: offset is a register | ||||
|             structure: registerOffset | ||||
|   immediateOffset: | ||||
|     bits: 12 | ||||
|     ranges: | ||||
|       11-0: | ||||
|         name: 12-bit immediate offset | ||||
|         description: unsigned number | ||||
|   registerOffset: | ||||
|     bits: 12 | ||||
|     ranges: | ||||
|       11-4: | ||||
|         name: shift | ||||
|         description: shift applied to Rm | ||||
|       3-0: | ||||
|         name: Rm | ||||
|         description: offset register | ||||
| ``` | ||||
| </details> | ||||
|  | ||||
| #### config.json | ||||
|  | ||||
|  | ||||
| #### dark.json | ||||
|  | ||||
|  | ||||
| #### blueprint.json | ||||
|  | ||||
|  | ||||
| #### transparent.json | ||||
|  | ||||
|  | ||||
| ## Config | ||||
|  | ||||
| The config file may change the following values to customize the layout and style: | ||||
| - `defaultFontFamily`: the default font family | ||||
| - `defaultFontSize`: the default font size | ||||
| - `italicFontFamily`: the italic font family (for value description) | ||||
| - `italicFontSize`: the italic font size | ||||
| - `backgroundColor`: the image background color (ex: [222, 250, 206]) | ||||
| - `textColor`: the default text color | ||||
| - `linkColor`: the color of linking lines and arrows | ||||
| - `borderColor`: the color of register borders | ||||
| - `bitWidth`: the width of 1 bit (in pixels) | ||||
| - `bitHeight`: the height of 1 bit (in pixels) | ||||
| - `descriptionMargin`: the space between descriptions (in pixels) | ||||
| - `dashLength`: the length of one dash (for dashed lines) | ||||
| - `dashSpace`: the space between dashes (for dashed lines) | ||||
| - `arrowSize`: the arrow size (height in pixels, width for horizontal arrow) | ||||
| - `margins`: the margins from the borders of the image (in pixels, [top, right, bottom, left]) | ||||
| - `arrowMargin`: the margin between arrows and registers (in pixels) | ||||
| - `valuesGap`: the gap between values (in pixels) | ||||
| - `arrowLabelDistance`: the distance between arrows and their label (in pixels) | ||||
| - `forceDescsOnSide`: if true, descriptions are placed on the side of the register, otherwise, they are placed as close as possible to the bit | ||||
| - `leftLabels`: if true, descriptions are put on the left, otherwise, they default to the right hand side | ||||
| - `width`: the image width (in pixels) | ||||
| - `height`: the image height (in pixels) | ||||
|  | ||||
| Some config examples are already included: | ||||
| - [config.json](config.json): Default configuration values (black on white) | ||||
| - [dark.json](dark.json): Dark theme (white on black) | ||||
| - [blueprint.json](blueprint.json): Blueprint theme (white on blue) | ||||
| - [transparent.json](transparent.json): Transparent background, for easy integration in other documents (grey on transparent) | ||||
| @@ -19,6 +19,7 @@ | ||||
|     "valuesGap": 5, | ||||
|     "arrowLabelDistance": 5, | ||||
|     "forceDescsOnSide": false, | ||||
|     "leftLabels": false, | ||||
|     "width": 1200, | ||||
|     "height": 800 | ||||
| } | ||||
							
								
								
									
										10
									
								
								config.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								config.py
									
									
									
									
									
								
							| @@ -1,3 +1,5 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| import json | ||||
| import re | ||||
|  | ||||
| @@ -29,8 +31,9 @@ class Config: | ||||
|      | ||||
|     def __init__(self, path: str = "config.json") -> None: | ||||
|         self.load(path) | ||||
|      | ||||
|     def load(self, path: str) -> None: | ||||
|  | ||||
|     @staticmethod | ||||
|     def load(path: str) -> None: | ||||
|         with open(path, "r") as f: | ||||
|             config = json.load(f) | ||||
|              | ||||
| @@ -39,5 +42,6 @@ class Config: | ||||
|                 if hasattr(Config, k): | ||||
|                     setattr(Config, k, v) | ||||
|  | ||||
|     @staticmethod | ||||
|     def formatKey(key: str) -> str: | ||||
|         return re.sub(r"([a-z])([A-Z])", r"\1_\2", key).upper() | ||||
|         return re.sub(r"([a-z])([A-Z])", r"\1_\2", key).upper() | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								example_blueprint.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_blueprint.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 63 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_dark.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_dark.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 62 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_normal.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_normal.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 60 KiB | 
							
								
								
									
										
											BIN
										
									
								
								example_transparent.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								example_transparent.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 43 KiB | 
							
								
								
									
										125
									
								
								format.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								format.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| # Schema Format | ||||
|  | ||||
| _**Supported syntaxes: JSON, XML, YAML**_ | ||||
|  | ||||
| The following description uses the JSON syntax | ||||
|  | ||||
| For examples in different formats, see [example1.yaml](example1.yaml), [example2.yaml](example2.yaml), [example3.json](example3.json) and [example4.xml](example4.xml). | ||||
|  | ||||
| ## Main layout | ||||
|  | ||||
| A schema contains a dictionary of structures. There must be at least one defined structure named "main"  | ||||
| ```json | ||||
| { | ||||
|   "structures": { | ||||
|     "main": { | ||||
|       ... | ||||
|     }, | ||||
|     "struct1": { | ||||
|       ... | ||||
|     }, | ||||
|     "struct2": { | ||||
|       ... | ||||
|     }, | ||||
|     ... | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Structure | ||||
|  | ||||
| A structure has a given number of bits and one or multiple ranges. Each range of bits can have a name, a description and / or values with special meaning (see [Range](#range)). A range's structure can also depend on another range's value (see [Dependencies](#dependencies)) | ||||
|  | ||||
| The range name (or key) defines the left- and rightmost bits (e.g. 7-4 goes from bit 7 to bit 4). Bits are displayed in big-endian, i.e. the leftmost bit has the highest value. | ||||
| ```json | ||||
| "main": { | ||||
|   "bits": 8, | ||||
|   "ranges": { | ||||
|     "7-4": { | ||||
|       ... | ||||
|     }, | ||||
|     "3-2": { | ||||
|       ... | ||||
|     }, | ||||
|     "1": { | ||||
|       ... | ||||
|     }, | ||||
|     "0": { | ||||
|       ... | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Range | ||||
|  | ||||
| A range represents a group of consecutive bits. It can have a name (display in the bit cells), a description (displayed under the structure) and / or values. | ||||
|  | ||||
| For values depending on other ranges, see [Dependencies](#dependencies). | ||||
|  | ||||
| > **Note**<br> | ||||
| > In YAML, make sure to wrap values in quotes because some values can be interpreted as octal notation (e.g. 010) | ||||
|  | ||||
| ```json | ||||
| "3-2": { | ||||
|   "name": "op", | ||||
|   "description": "Logical operation", | ||||
|   "values": { | ||||
|     "00": "AND", | ||||
|     "01": "OR", | ||||
|     "10": "XOR", | ||||
|     "11": "NAND" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Dependencies | ||||
|  | ||||
| The structure of one range may depend on the value of another. To represent this situation, first indicate on the child range the range on which it depends: | ||||
|  | ||||
| ```json | ||||
| "7-4": { | ||||
|   ... | ||||
|   "depends-on": "0" | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Then, in its values, indicate which structure to use. A description can also be added (displayed above the horizontal dependency arrow) | ||||
|  | ||||
| ```json | ||||
| "7-4": { | ||||
|   ... | ||||
|   "depends-on": "0", | ||||
|   "values": { | ||||
|     "0": { | ||||
|       "description": "immediate value", | ||||
|       "structure": "immediateValue" | ||||
|     }, | ||||
|     "1": { | ||||
|       "description": "value in register", | ||||
|       "structure": "registerValue" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Finally, add the sub-structures to the structure dictionary: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "structures": { | ||||
|     "main": { | ||||
|       ... | ||||
|     }, | ||||
|     "immediateValue": { | ||||
|       "bits": 4, | ||||
|       ... | ||||
|     }, | ||||
|     "registerValue": { | ||||
|       "bits": 4, | ||||
|       ... | ||||
|     }, | ||||
|     ... | ||||
|   } | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										10
									
								
								main.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										10
									
								
								main.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -1,9 +1,13 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| from __future__ import annotations | ||||
|  | ||||
| import argparse | ||||
| import os | ||||
|  | ||||
| from schema import InstructionSetSchema | ||||
|  | ||||
|  | ||||
| description = """Examples: | ||||
|     - Default theme (black on white): | ||||
|         python main.py schema.xml -o out.jpg | ||||
| @@ -22,7 +26,7 @@ description = """Examples: | ||||
| """ | ||||
|  | ||||
|  | ||||
| def processFile(inPath, outPath, confPath, display): | ||||
| def processFile(inPath: str, outPath: str, confPath: str, display: bool) -> None: | ||||
|     schema = InstructionSetSchema(inPath, confPath, display) | ||||
|     schema.save(outPath) | ||||
|  | ||||
| @@ -63,4 +67,4 @@ if __name__ == "__main__": | ||||
|         if output is None: | ||||
|             output = os.path.splitext(args.schema)[0] + ".png" | ||||
|          | ||||
|         processFile(args.schema, output, args.config, args.display) | ||||
|         processFile(args.schema, output, args.config, args.display) | ||||
|   | ||||
							
								
								
									
										31
									
								
								range.py
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								range.py
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| from __future__ import annotations | ||||
|  | ||||
| from typing import Union | ||||
| from typing import Union, Optional | ||||
|  | ||||
|  | ||||
| class Range: | ||||
| @@ -9,22 +9,23 @@ class Range: | ||||
|                  end: int, | ||||
|                  name: str, | ||||
|                  description: str = "", | ||||
|                  values: dict[str, Union[str, dict]] = None, | ||||
|                  dependsOn: str = None) -> None: | ||||
|                  values: Optional[dict[str, Union[str, dict]]] = None, | ||||
|                  dependsOn: Optional[tuple[int, int]] = None) -> None: | ||||
|          | ||||
|         self.start = start | ||||
|         self.end = end | ||||
|         self.name = name | ||||
|         self.description = description | ||||
|         self.values = values | ||||
|         self.dependsOn = dependsOn | ||||
|         self.lastValueY = -1 | ||||
|         self.start: int = start | ||||
|         self.end: int = end | ||||
|         self.name: str = name | ||||
|         self.description: str = description | ||||
|         self.values: Optional[dict[str, Union[str, dict]]] = values | ||||
|         self.dependsOn: Optional[tuple[int, int]] = dependsOn | ||||
|         self.lastValueY: int = -1 | ||||
|      | ||||
|     @property | ||||
|     def bits(self) -> int: | ||||
|         return self.end - self.start + 1 | ||||
|      | ||||
|     def load(start: int, end: int, data: dict): | ||||
|  | ||||
|     @staticmethod | ||||
|     def load(start: int, end: int, data: dict) -> Range: | ||||
|         values = None | ||||
|         bits = end - start + 1 | ||||
|          | ||||
| @@ -45,9 +46,11 @@ class Range: | ||||
|                      values, | ||||
|                      dependsOn) | ||||
|  | ||||
|     @staticmethod | ||||
|     def parseSpan(span: str) -> tuple[int, int]: | ||||
|         startEnd = span.split("-") | ||||
|         if len(startEnd) == 1: startEnd.append(startEnd[0]) | ||||
|         if len(startEnd) == 1: | ||||
|             startEnd.append(startEnd[0]) | ||||
|         start = int(startEnd[1]) | ||||
|         end = int(startEnd[0]) | ||||
|         return (start, end) | ||||
|         return (start, end) | ||||
|   | ||||
							
								
								
									
										27
									
								
								renderer.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								renderer.py
									
									
									
									
									
								
							| @@ -16,17 +16,23 @@ if TYPE_CHECKING: | ||||
|  | ||||
| class Renderer: | ||||
|     def __init__(self, config: Config, display: bool = False) -> None: | ||||
|         self.config = config | ||||
|         self.display = display | ||||
|         self.config: Config = config | ||||
|         self.display: bool = display | ||||
|         pygame.init() | ||||
|         if self.display: | ||||
|             self.win = pygame.display.set_mode([self.config.WIDTH, self.config.HEIGHT]) | ||||
|             self.win: pygame.Surface = pygame.display.set_mode([self.config.WIDTH, self.config.HEIGHT]) | ||||
|          | ||||
|         self.surf = pygame.Surface([self.config.WIDTH, self.config.HEIGHT], pygame.SRCALPHA) | ||||
|         self.font = pygame.font.SysFont(self.config.DEFAULT_FONT_FAMILY, self.config.DEFAULT_FONT_SIZE) | ||||
|         self.italicFont = pygame.font.SysFont(self.config.ITALIC_FONT_FAMILY, self.config.ITALIC_FONT_SIZE, italic=True) | ||||
|         self.surf: pygame.Surface = pygame.Surface([self.config.WIDTH, self.config.HEIGHT], pygame.SRCALPHA) | ||||
|  | ||||
|         self.font: pygame.font.Font = pygame.font.SysFont( | ||||
|             self.config.DEFAULT_FONT_FAMILY, | ||||
|             self.config.DEFAULT_FONT_SIZE) | ||||
|  | ||||
|         self.italicFont: pygame.font.Font = pygame.font.SysFont( | ||||
|             self.config.ITALIC_FONT_FAMILY, | ||||
|             self.config.ITALIC_FONT_SIZE, italic=True) | ||||
|          | ||||
|         self.margins = self.config.MARGINS | ||||
|         self.margins: list[int, int, int, int] = self.config.MARGINS | ||||
|      | ||||
|     def render(self, schema: InstructionSetSchema) -> None: | ||||
|         self.surf.fill(self.config.BACKGROUND_COLOR) | ||||
| @@ -206,8 +212,7 @@ class Renderer: | ||||
|  | ||||
|     def drawDependency(self, | ||||
|                        struct: Structure, | ||||
|                        structures: dict[str, | ||||
|                        Structure], | ||||
|                        structures: dict[str, Structure], | ||||
|                        bitsX: float, | ||||
|                        bitsY: float, | ||||
|                        range_: Range, | ||||
| @@ -274,7 +279,7 @@ class Renderer: | ||||
|                            descY + bitH - arrowMargin) | ||||
|  | ||||
|             prevDependY = descY + bitH*2 + arrowMargin | ||||
|             prevRangeY =prevDependY | ||||
|             prevRangeY = prevDependY | ||||
|             dependRange.lastValueY = prevDependY | ||||
|             descY = self.drawStructure(structures[data["structure"]], structures, rStartX, descY) | ||||
|          | ||||
| @@ -315,4 +320,4 @@ class Renderer: | ||||
|             self.surf.blit(txt, [ | ||||
|                 (start.x + end.x - txt.get_width())/2, | ||||
|                 (start.y + end.y)/2 + arrowLabelDist | ||||
|             ]) | ||||
|             ]) | ||||
|   | ||||
| @@ -17,9 +17,10 @@ class InstructionSetSchema: | ||||
|     VALID_EXTENSIONS = ("yaml", "json", "xml") | ||||
|      | ||||
|     def __init__(self, path: str, configPath: str = "config.json", display: bool = False) -> None: | ||||
|         self.config = Config(configPath) | ||||
|         self.display = display | ||||
|         self.path = path | ||||
|         self.config: Config = Config(configPath) | ||||
|         self.display: bool = display | ||||
|         self.path: str = path | ||||
|         self.structures: dict[str, Structure] = {} | ||||
|         self.load() | ||||
|      | ||||
|     def load(self) -> None: | ||||
| @@ -51,4 +52,4 @@ class InstructionSetSchema: | ||||
|         renderer.render(self) | ||||
|         renderer.save(path) | ||||
|         if self.display: | ||||
|             input("Press ENTER to quit") | ||||
|             input("Press ENTER to quit") | ||||
|   | ||||
							
								
								
									
										15
									
								
								structure.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								structure.py
									
									
									
									
									
								
							| @@ -7,14 +7,15 @@ class Structure: | ||||
|     def __init__(self, | ||||
|                  name: str, | ||||
|                  bits: int, | ||||
|                  ranges: dict[str, Range], | ||||
|                  ranges: dict[tuple[int, int], Range], | ||||
|                  start: int = 0) -> None: | ||||
|          | ||||
|         self.name = name | ||||
|         self.bits = bits | ||||
|         self.ranges = ranges | ||||
|         self.start = start | ||||
|      | ||||
|         self.name: str = name | ||||
|         self.bits: int = bits | ||||
|         self.ranges: dict[tuple[int, int], Range] = ranges | ||||
|         self.start: int = start | ||||
|  | ||||
|     @staticmethod | ||||
|     def load(id_: str, data: dict) -> Structure: | ||||
|         ranges = {} | ||||
|         for rSpan, rData in data["ranges"].items(): | ||||
| @@ -34,4 +35,4 @@ class Structure: | ||||
|  | ||||
|     def getSortedRanges(self) -> list[Range]: | ||||
|         ranges = self.ranges.values() | ||||
|         return list(sorted(ranges, key=lambda r: r.end)) | ||||
|         return list(sorted(ranges, key=lambda r: r.end)) | ||||
|   | ||||
							
								
								
									
										9
									
								
								vec.py
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								vec.py
									
									
									
									
									
								
							| @@ -5,8 +5,8 @@ from math import sqrt | ||||
|  | ||||
| class Vec: | ||||
|     def __init__(self, x: float = 0, y: float = 0) -> None: | ||||
|         self.x = x | ||||
|         self.y = y | ||||
|         self.x: float = x | ||||
|         self.y: float = y | ||||
|      | ||||
|     def __add__(self, v: Vec) -> Vec: | ||||
|         return Vec(self.x+v.x, self.y+v.y) | ||||
| @@ -25,5 +25,6 @@ class Vec: | ||||
|      | ||||
|     def norm(self) -> Vec: | ||||
|         mag = self.mag() | ||||
|         if mag == 0: return Vec() | ||||
|         return self/mag | ||||
|         if mag == 0: | ||||
|             return Vec() | ||||
|         return self / mag | ||||
|   | ||||
| @@ -7,7 +7,9 @@ from bs4 import BeautifulSoup | ||||
| if TYPE_CHECKING: | ||||
|     from io import TextIOWrapper | ||||
|  | ||||
|  | ||||
| class XMLLoader: | ||||
|     @staticmethod | ||||
|     def load(file_: TextIOWrapper) -> dict: | ||||
|         schema = {} | ||||
|         bs = BeautifulSoup(file_.read(), "xml") | ||||
| @@ -19,10 +21,12 @@ class XMLLoader: | ||||
|          | ||||
|         schema["structures"] = structures | ||||
|         return schema | ||||
|      | ||||
|  | ||||
|     @staticmethod | ||||
|     def parseStructure(structElmt: any) -> dict: | ||||
|         struct = {} | ||||
|         struct["bits"] = structElmt.get("bits") | ||||
|         struct = { | ||||
|             "bits": structElmt.get("bits") | ||||
|         } | ||||
|         ranges = {} | ||||
|         rangeElmts = structElmt.findAll("range") | ||||
|         for rangeElmt in rangeElmts: | ||||
| @@ -32,11 +36,14 @@ class XMLLoader: | ||||
|         struct["ranges"] = ranges | ||||
|         return struct | ||||
|  | ||||
|     @staticmethod | ||||
|     def parseRange(rangeElmt: any) -> dict: | ||||
|         range_ = {} | ||||
|         range_["name"] = rangeElmt.get("name") | ||||
|         range_ = { | ||||
|             "name": rangeElmt.get("name") | ||||
|         } | ||||
|         desc = rangeElmt.find("description") | ||||
|         if desc is not None: range_["description"] = desc.getText() | ||||
|         if desc is not None: | ||||
|             range_["description"] = desc.getText() | ||||
|          | ||||
|         valuesElmt = rangeElmt.find("values") | ||||
|         if valuesElmt is not None: | ||||
| @@ -47,6 +54,7 @@ class XMLLoader: | ||||
|          | ||||
|         return range_ | ||||
|  | ||||
|     @staticmethod | ||||
|     def parseValues(valuesElmt: any) -> dict: | ||||
|         values = {} | ||||
|         caseElmts = valuesElmt.findAll("case") | ||||
| @@ -64,4 +72,4 @@ class XMLLoader: | ||||
|             else: | ||||
|                 values[val] = desc | ||||
|  | ||||
|         return values | ||||
|         return values | ||||
|   | ||||
		Reference in New Issue
	
	Block a user