flipjump 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. flipjump/README.md +95 -0
  2. flipjump/__init__.py +23 -0
  3. flipjump/assembler/__init__.py +0 -0
  4. flipjump/assembler/assembler.py +241 -0
  5. flipjump/assembler/fj_parser.py +728 -0
  6. flipjump/assembler/inner_classes/__init__.py +0 -0
  7. flipjump/assembler/inner_classes/expr.py +129 -0
  8. flipjump/assembler/inner_classes/ops.py +369 -0
  9. flipjump/assembler/preprocessor.py +292 -0
  10. flipjump/fjm/__init__.py +0 -0
  11. flipjump/fjm/fjm_consts.py +66 -0
  12. flipjump/fjm/fjm_reader.py +187 -0
  13. flipjump/fjm/fjm_writer.py +189 -0
  14. flipjump/flipjump_cli.py +345 -0
  15. flipjump/flipjump_quickstart.py +342 -0
  16. flipjump/interpretter/__init__.py +0 -0
  17. flipjump/interpretter/debugging/__init__.py +0 -0
  18. flipjump/interpretter/debugging/breakpoints.py +234 -0
  19. flipjump/interpretter/debugging/macro_usage_graph.py +98 -0
  20. flipjump/interpretter/fjm_run.py +183 -0
  21. flipjump/interpretter/io_devices/BrokenIO.py +15 -0
  22. flipjump/interpretter/io_devices/FixedIO.py +51 -0
  23. flipjump/interpretter/io_devices/IODevice.py +16 -0
  24. flipjump/interpretter/io_devices/StandardIO.py +57 -0
  25. flipjump/interpretter/io_devices/__init__.py +0 -0
  26. flipjump/stl/README.md +146 -0
  27. flipjump/stl/bit/casting.fj +168 -0
  28. flipjump/stl/bit/cond_jumps.fj +97 -0
  29. flipjump/stl/bit/input.fj +29 -0
  30. flipjump/stl/bit/logics.fj +128 -0
  31. flipjump/stl/bit/math.fj +85 -0
  32. flipjump/stl/bit/memory.fj +113 -0
  33. flipjump/stl/bit/output.fj +221 -0
  34. flipjump/stl/bit/pointers.fj +181 -0
  35. flipjump/stl/bit/shifts.fj +57 -0
  36. flipjump/stl/casting.fj +52 -0
  37. flipjump/stl/conf.json +31 -0
  38. flipjump/stl/hex/advanced_pointers.fj +153 -0
  39. flipjump/stl/hex/cond_jumps.fj +162 -0
  40. flipjump/stl/hex/input.fj +87 -0
  41. flipjump/stl/hex/logics.fj +257 -0
  42. flipjump/stl/hex/math.fj +281 -0
  43. flipjump/stl/hex/math_basic.fj +207 -0
  44. flipjump/stl/hex/memory.fj +122 -0
  45. flipjump/stl/hex/output.fj +214 -0
  46. flipjump/stl/hex/pointers.fj +227 -0
  47. flipjump/stl/hex/shifts.fj +83 -0
  48. flipjump/stl/hex/tables_init.fj +78 -0
  49. flipjump/stl/mathlib.fj +516 -0
  50. flipjump/stl/ptrlib.fj +87 -0
  51. flipjump/stl/runlib.fj +163 -0
  52. flipjump/utils/__init__.py +0 -0
  53. flipjump/utils/classes.py +94 -0
  54. flipjump/utils/constants.py +17 -0
  55. flipjump/utils/exceptions.py +54 -0
  56. flipjump/utils/functions.py +74 -0
  57. flipjump-1.2.0.dist-info/LICENSE +25 -0
  58. flipjump-1.2.0.dist-info/METADATA +275 -0
  59. flipjump-1.2.0.dist-info/RECORD +61 -0
  60. flipjump-1.2.0.dist-info/WHEEL +4 -0
  61. flipjump-1.2.0.dist-info/entry_points.txt +3 -0
flipjump/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # FlipJump Source Code
2
+
3
+ In this documentation file you could find information about every python source file in the flipjump module.
4
+
5
+ ## The FlipJump Macro-Assembler
6
+
7
+ The assembler has 4 steps:
8
+ - parsing the .fj text files into a dictionary of macros and their ops ([fj_parser.py](assembler/fj_parser.py)).
9
+ - resolving (unwinding) the macros (and reps) to get a straight stream of ops ([preprocessor.py](assembler/preprocessor.py)).
10
+ - resolving the label values and getting the ops binary data ([assembler.py](assembler/assembler.py)).
11
+ - writing the binary data into the executable ([fjm_writer.py](fjm/fjm_writer.py)).
12
+
13
+ The whole process is executed within the [assemble()](assembler/assembler.py) function.
14
+ ![Assembly of calc.fj](../res/calc__asm.jpg)
15
+
16
+ - The [ops.py](assembler/inner_classes/ops.py) file contains the classes of the different assembly ops.
17
+ - The [expr.py](assembler/inner_classes/expr.py) file contains the expression class (Expr), which is the code's representation of the assembly mathematical expressions. The expressions are based on numbers and labels.
18
+
19
+ ## The FlipJump Interpreter
20
+
21
+ The Interpreter ([fjm_run.py](interpretter/fjm_run.py)) stores the entire memory in a dictionary {address: value}, and supports unaligned-word access.
22
+
23
+ The whole interpretation is done within the [run()](interpretter/fjm_run.py) function (also uses the [fjm_reader.py](fjm/fjm_reader.py) to read the fjm file - i.e. to get the flipjump program memory from the compiled fjm file).
24
+ More about [how to run](../README.md#how-to-run).
25
+ ![Running the compiled calculator](../res/calc__run.jpg)
26
+
27
+ The Interpreter has a built-in debugger, and it's activated by specifying breakpoints when called (via the [breakpoints.py](interpretter/debugging/breakpoints.py)'s `BreakpointHandler`).
28
+ The debugger can stop on the next breakpoint, or on a fixed number of executed ops after the current breakpoint.
29
+ In order to call the debugger with the right labels, get familiar with the [generating label names](README.md#Generated-Label-Names) (and see the debugger-image there), and use the `-d`/`-b`/`-B` cli options.
30
+ More about [how to debug](../README.md#how-to-debug).
31
+
32
+ The [macro_usage_graph.py](interpretter/debugging/macro_usage_graph.py) file exports a feature to present the macro-usage (which are the most used macros, and what % do they take from the overall flipjump ops) in a graph.
33
+ In order to view it, run the assembler with `--stats` (requires plotly to be installed (installed automatically with `pip install flipjump[stats]`)).
34
+ For example:
35
+ ![The macro-usage statistics of calc.fj](../res/calc_stats.png)
36
+
37
+ ## The Using-FlipJump Files
38
+
39
+ - The [flipjump_cli.py](flipjump_cli.py) file is the main FlipJump cli-script. run with --help to see its capabilities. The `fj` utility runs the main() of this file.
40
+ - The [flipjump_quickstart.py](flipjump_quickstart.py) file contains the fundamental assemble/run functions that are exposed to the users. They are wrappers to the inner api. These are the functions that will be exported when you `import flipjump`.
41
+
42
+ ### FJM versions
43
+
44
+ The .fjm file currently has 4 versions:
45
+
46
+ - Version 0: The basic version
47
+ - Version 1: The normal version (more configurable than the basic version)
48
+ - Version 2: The relative-jumps version (good for further compression)
49
+ - Version 3: The compressed version
50
+
51
+ You can specify the version you want with the `-v VERSION` flag.
52
+ The assembler chooses **by default** version **3** if the `--outfile` is specified, and version **1** if it isn't.
53
+
54
+ ### Generated Label Names
55
+
56
+ The generated label string is a concatenation of the macro call tree, each separated by '---', and finish with the label local-name.
57
+
58
+ Each macro call string is as follows:\
59
+ short_file_name **:** line **:** macro_called
60
+
61
+ So if a->bit.b->hex.c->my_label: (a, bit.b called from file f2 lines 3,5; hex.c from file s1, line 72), the label's name will be:\
62
+ f2:3:a---f2:5:bit.b---s1:72:hex.c---my_label
63
+
64
+ On a rep-call (on index==i), the macro call string is:\
65
+ short_file_name : line : rep{i} : macro_called\
66
+ for example: f1:32:rep6:hex.print---f2:17:print_bit---print_label
67
+
68
+ the short_file_name is (by default) s1,s2,s3,... for the standard library files (in the order of [stl/conf.json - all](stl/conf.json)),
69
+ and f1,f2,f3,... for the compiled .fj files, in the order they are mentioned to the compiler (or appear in the test-line).
70
+
71
+ You can place breakpoints to stop on specific labels using the `-d`, followed by a `-b` and a label name (or `-B` and a part of a label name). For example:
72
+ ![Debugging Demo](../res/breakpoint.jpg)
73
+
74
+ ## More Files
75
+
76
+ - The [fjm_consts.py](fjm/fjm_consts.py) contains the constants needed for interacting with the fjm format (used by the [fjm_reader.py](fjm/fjm_reader.py) + [fjm_writer.py](fjm/fjm_writer.py)).
77
+ - The [utils/](utils) folder contains common utilities used and shared by the entire project:
78
+ - [utils/classes.py](utils/classes.py) - contains the common classes used in the entire project
79
+ - [utils/functions.py](utils/functions.py) - contains the common utility functions used in the entire project
80
+ - [utils/constants.py](utils/constants.py) - contains the project's constants and definitions.
81
+ - [utils/exceptions.py](utils/exceptions.py) - contains all the project's exceptions.
82
+ - The [interpreter/io_devices/](interpretter/io_devices) folder contains modules for different Input/Output-handling classes (can be passed as a parameter to the interpreter).
83
+ - The standard one is [StandardIO.py](interpretter/io_devices/StandardIO.py), which takes its input from the standard input, and write its output to the standard output.
84
+ - The tests use the [FixedIO.py](interpretter/io_devices/FixedIO.py), which takes a defined input and remembers its output.
85
+ - If you want to assert that your program takes no input and generates no output, use the [BrokenIO.py](interpretter/io_devices/BrokenIO.py), which raises exception on every input/output.
86
+ - Finally, the pure abstract IO handler class - [IODevice.py](interpretter/io_devices/IODevice.py).
87
+
88
+ # Read More
89
+
90
+ The FlipJump source is built in a way that allows simple addition of new features.
91
+
92
+ Every addition should be supported from the parsing level, up to the phase that is disappears (and probably is replaced with some flipjump ops). See the `assemble()` function in [assembler](assembler/assembler.py) to better understand the assembler 'pipeline'.
93
+
94
+ For example, if you want to add a new operation `a@b` that calculates _a^2+b^2_ or `a!` for _factorial(a)_, it is simple as adding a parsing rule in [fj_parser.py](assembler/fj_parser.py), and then adding the function to the op_string_to_function() in [expr.py](assembler/inner_classes/expr.py). That's it.
95
+
flipjump/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ from flipjump.flipjump_cli import assemble_run_according_to_cmd_line_args
2
+ from flipjump.flipjump_quickstart import (assemble, run, debug, run_test_output,
3
+ assemble_and_run, assemble_and_debug, assemble_and_run_test_output,
4
+ FJMVersion, TerminationCause, TerminationStatistics,
5
+ FixedIO, IODevice, StandardIO)
6
+ from flipjump.utils.exceptions import IODeviceException, FlipJumpException
7
+ from flipjump.interpretter.io_devices.BrokenIO import BrokenIO
8
+
9
+
10
+ __all__ = [
11
+ 'assemble_run_according_to_cmd_line_args',
12
+ 'assemble',
13
+ 'run',
14
+ 'debug',
15
+ 'run_test_output',
16
+ 'assemble_and_run',
17
+ 'assemble_and_debug',
18
+ 'assemble_and_run_test_output',
19
+ 'FJMVersion',
20
+ 'TerminationCause',
21
+ 'TerminationStatistics',
22
+ 'FlipJumpException',
23
+ ]
File without changes
@@ -0,0 +1,241 @@
1
+ import dataclasses
2
+ from collections import defaultdict
3
+ from pathlib import Path
4
+ from typing import Deque, List, Dict, Tuple, Optional
5
+
6
+ from flipjump.fjm.fjm_writer import Writer
7
+ from flipjump.utils.constants import WFLIP_LABEL_PREFIX
8
+ from flipjump.utils.functions import save_debugging_labels
9
+ from flipjump.utils.classes import PrintTimer
10
+ from flipjump.assembler.fj_parser import parse_macro_tree
11
+ from flipjump.utils.exceptions import FlipJumpAssemblerException, FlipJumpException, FlipJumpWriteFjmException
12
+ from flipjump.assembler.inner_classes.ops import FlipJump, WordFlip, LastPhaseOp, NewSegment, ReserveBits, Padding
13
+ from flipjump.assembler.preprocessor import resolve_macros
14
+
15
+
16
+ def assert_address_in_memory(memory_width: int, address: int):
17
+ if address < 0 or address >= (1 << memory_width):
18
+ raise FlipJumpAssemblerException(f"Not enough space with the {memory_width}-bits memory-width.")
19
+
20
+
21
+ def validate_addresses(memory_width, first_address, last_address):
22
+ if first_address % memory_width != 0 or last_address % memory_width != 0:
23
+ raise FlipJumpAssemblerException(f'segment boundaries are unaligned: '
24
+ f'[{hex(first_address)}, {hex(last_address - 1)}].')
25
+
26
+ assert_address_in_memory(memory_width, first_address)
27
+ assert_address_in_memory(memory_width, last_address)
28
+
29
+
30
+ def add_segment_to_fjm(memory_width: int,
31
+ fjm_writer: Writer,
32
+ first_address: int, last_address: int,
33
+ fj_words: List[int], wflip_words: List[int]) -> None:
34
+ validate_addresses(memory_width, first_address, last_address)
35
+ if first_address == last_address:
36
+ return
37
+
38
+ data_words = fj_words + wflip_words
39
+ data_start = fjm_writer.add_data(data_words)
40
+
41
+ segment_start_address = first_address // memory_width
42
+ segment_length = (last_address - first_address) // memory_width
43
+
44
+ try:
45
+ fjm_writer.add_segment(segment_start_address, segment_length, data_start, len(data_words))
46
+ except FlipJumpWriteFjmException as e:
47
+ exception_message = (f"failed to add the segment: "
48
+ f"{fjm_writer.get_segment_addresses_repr(segment_start_address, segment_length)}.")
49
+ raise FlipJumpAssemblerException(exception_message) from e
50
+
51
+ fj_words.clear()
52
+ wflip_words.clear()
53
+
54
+
55
+ @dataclasses.dataclass
56
+ class WFlipSpot:
57
+ list: List[int]
58
+ index: int
59
+ address: int
60
+
61
+
62
+ class BinaryData:
63
+ def __init__(self, memory_width: int, first_segment: NewSegment, labels: Dict[str, int]):
64
+ self.memory_width = memory_width
65
+
66
+ self.first_address = first_segment.start_address
67
+ self.wflip_address = first_segment.wflip_start_address
68
+
69
+ self.labels = labels
70
+ self.wflips_so_far = 0
71
+
72
+ self.current_address = self.first_address
73
+
74
+ self.fj_words: List[int] = []
75
+ self.wflip_words: List[int] = []
76
+
77
+ self.padding_ops_indices: List[int] = [] # indices in self.fj_words
78
+
79
+ # return_address -> { (f3, f2, f1, f0) -> start_flip_address }
80
+ self.wflips_dict: Dict[int, Dict[Tuple[int, ...]]] = defaultdict(lambda: {})
81
+
82
+ def get_wflip_spot(self) -> WFlipSpot:
83
+ if self.padding_ops_indices:
84
+ index = self.padding_ops_indices.pop()
85
+ return WFlipSpot(self.fj_words, index, self.first_address + self.memory_width * index)
86
+
87
+ wflip_spot = WFlipSpot(self.wflip_words, len(self.wflip_words), self.wflip_address)
88
+ self.wflip_words += (0, 0)
89
+ self.wflip_address += 2*self.memory_width
90
+ return wflip_spot
91
+
92
+ def close_and_add_segment(self, fjm_writer: Writer) -> None:
93
+ add_segment_to_fjm(self.memory_width, fjm_writer, self.first_address, self.wflip_address, self.fj_words, self.wflip_words)
94
+
95
+ def _insert_wflip_label(self, address: int):
96
+ self.labels[f'{WFLIP_LABEL_PREFIX}{self.wflips_so_far}'] = address
97
+ self.wflips_so_far += 1
98
+
99
+ def insert_fj_op(self, flip: int, jump: int) -> None:
100
+ self.fj_words += (flip, jump)
101
+ self.current_address += 2*self.memory_width
102
+
103
+ def insert_wflip_ops(self, word_address: int, flip_value: int, return_address: int) -> None:
104
+ if 0 == flip_value:
105
+ self.insert_fj_op(0, return_address)
106
+ else:
107
+ assert_address_in_memory(self.memory_width, flip_value)
108
+
109
+ return_dict = self.wflips_dict[return_address]
110
+
111
+ # this is the order of flip_addresses (tested with many other orders) that produces the best
112
+ # found-statistic for searching flip_bit[:i] with different i's in return_dict.
113
+ flip_addresses = [word_address + i for i in range(self.memory_width) if flip_value & (1 << i)][::-1]
114
+
115
+ # insert the first op
116
+ self.insert_fj_op(flip_addresses.pop(), 0)
117
+ last_return_address_index = self.fj_words, len(self.fj_words) - 1
118
+
119
+ while flip_addresses:
120
+ flips_key = tuple(flip_addresses)
121
+ ops_list, last_address_index = last_return_address_index
122
+
123
+ if flips_key in return_dict:
124
+ # connect the last op to the already created wflip-chain
125
+ ops_list[last_address_index] = return_dict[flips_key]
126
+ return
127
+ else:
128
+ # insert a new wflip op, and connect the last one to it
129
+ wflip_spot = self.get_wflip_spot()
130
+ self._insert_wflip_label(wflip_spot.address)
131
+
132
+ ops_list[last_address_index] = wflip_spot.address
133
+ return_dict[flips_key] = wflip_spot.address
134
+
135
+ wflip_spot.list[wflip_spot.index] = flip_addresses.pop()
136
+ last_return_address_index = wflip_spot.list, wflip_spot.index + 1
137
+
138
+ ops_list, last_address_index = last_return_address_index
139
+ ops_list[last_address_index] = return_address
140
+
141
+ def insert_padding(self, ops_count: int) -> None:
142
+ for i in range(len(self.fj_words), len(self.fj_words) + 2 * ops_count, 2):
143
+ self.padding_ops_indices.append(i)
144
+ self.fj_words += (0, 0)
145
+ self.current_address += ops_count * (2 * self.memory_width)
146
+
147
+ def insert_new_segment(self, fjm_writer: Writer, first_address: int, wflip_first_address: int) -> None:
148
+ self.close_and_add_segment(fjm_writer)
149
+
150
+ self.first_address = first_address
151
+ self.wflip_address = wflip_first_address
152
+ self.current_address = self.first_address
153
+
154
+ self.padding_ops_indices.clear()
155
+
156
+ def insert_reserve_bits(self, fjm_writer: Writer, new_first_address: int) -> None:
157
+ add_segment_to_fjm(self.memory_width, fjm_writer, self.first_address, new_first_address, self.fj_words, [])
158
+
159
+ self.first_address = new_first_address
160
+ self.current_address = self.first_address
161
+
162
+ self.padding_ops_indices.clear()
163
+
164
+
165
+ def labels_resolve(ops: Deque[LastPhaseOp], labels: Dict[str, int],
166
+ memory_width: int, fjm_writer: Writer) -> None:
167
+ """
168
+ resolve the labels and expressions to get the list of fj ops, and add all the data and segments into the fjm_writer.
169
+ @param ops:[in]: the list ops returned from the preprocessor stage
170
+ @param labels:[in]: dictionary from label to its resolved value
171
+ @param memory_width: the memory-width
172
+ @param fjm_writer: [out]: the .fjm file writer
173
+ """
174
+ first_segment: NewSegment = ops.popleft()
175
+ if not isinstance(first_segment, NewSegment):
176
+ raise FlipJumpAssemblerException(f"The first op must be of type NewSegment (and not {first_segment}).")
177
+
178
+ binary_data = BinaryData(memory_width, first_segment, labels)
179
+
180
+ for op in ops:
181
+ if isinstance(op, FlipJump):
182
+ try:
183
+ binary_data.insert_fj_op(op.get_flip(labels), op.get_jump(labels))
184
+ except FlipJumpException as e:
185
+ raise FlipJumpAssemblerException(f"{e} in op {op}.")
186
+
187
+ elif isinstance(op, WordFlip):
188
+ try:
189
+ binary_data.insert_wflip_ops(op.get_word_address(labels), op.get_flip_value(labels),
190
+ op.get_return_address(labels))
191
+ except FlipJumpException as e:
192
+ raise FlipJumpAssemblerException(f"{e} in op {op}.")
193
+
194
+ elif isinstance(op, Padding):
195
+ binary_data.insert_padding(op.ops_count)
196
+
197
+ elif isinstance(op, NewSegment):
198
+ binary_data.insert_new_segment(fjm_writer, op.start_address, op.wflip_start_address)
199
+
200
+ elif isinstance(op, ReserveBits):
201
+ binary_data.insert_reserve_bits(fjm_writer, op.first_address_after_reserved)
202
+
203
+ else:
204
+ raise FlipJumpAssemblerException(f"Can't resolve/assemble the next opcode - {str(op)}")
205
+
206
+ binary_data.close_and_add_segment(fjm_writer)
207
+
208
+
209
+ def assemble(input_files: List[Tuple[str, Path]], memory_width: int, fjm_writer: Writer, *,
210
+ warning_as_errors: bool = True, debugging_file_path: Optional[Path] = None,
211
+ show_statistics: bool = False, print_time: bool = True)\
212
+ -> None:
213
+ """
214
+ runs the assembly pipeline. assembles the input files to a .fjm.
215
+ :param input_files:[in]: a list of (short_file_name, fj_file_path). The files will to be parsed in that given order.
216
+ :param memory_width: the memory-width
217
+ :param fjm_writer:[out]: the .fjm file writer
218
+ :param warning_as_errors: treat warnings as errors (stop execution on warnings)
219
+ :param debugging_file_path:[out]: is specified, save debug information in this file
220
+ :param show_statistics: if true shows macro-usage statistics
221
+ :param print_time: if true prints the times of each assemble-stage
222
+ """
223
+ try:
224
+ with PrintTimer(' parsing: ', print_time=print_time):
225
+ macros = parse_macro_tree(input_files, memory_width, warning_as_errors)
226
+
227
+ with PrintTimer(' macro resolve: ', print_time=print_time):
228
+ ops, labels = resolve_macros(memory_width, macros, show_statistics=show_statistics)
229
+
230
+ with PrintTimer(' labels resolve: ', print_time=print_time):
231
+ labels_resolve(ops, labels, memory_width, fjm_writer)
232
+
233
+ with PrintTimer(' create binary: ', print_time=print_time):
234
+ fjm_writer.write_to_file()
235
+ save_debugging_labels(debugging_file_path, labels)
236
+
237
+ except FlipJumpException as fj_exception:
238
+ raise fj_exception
239
+ except Exception as unknown_exception:
240
+ raise FlipJumpAssemblerException("Unknown exception during assembling the .fj files, please report this bug") \
241
+ from unknown_exception