AssemblyCraftCompiler 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.
asmcc/__init__.py ADDED
@@ -0,0 +1,238 @@
1
+ __version__ = "1.2.0"
2
+ import struct
3
+ import re
4
+
5
+ class AssemblyCraftCompilerError(Exception):
6
+ pass
7
+
8
+ class AssemblyCraftCompiler:
9
+ MAGIC_HEADER = (b"ACX"+__version__.split(".")[0].encode())
10
+
11
+ ALLOC_SIZES = {
12
+ 'ds': 16,
13
+ 'dm': 32,
14
+ 'db': 64
15
+ }
16
+
17
+ REGISTERS = {
18
+ 'rax': 0x00, 'rbx': 0x01, 'rcx': 0x02, 'rdx': 0x03,
19
+ 'rsi': 0x04, 'rdi': 0x05, 'rbp': 0x06, 'rsp': 0x07,
20
+ 'r8': 0x08, 'r9': 0x09, 'r10': 0x0A, 'r11': 0x0B,
21
+ 'r12': 0x0C, 'r13': 0x0D, 'r14': 0x0E, 'r15': 0x0F
22
+ }
23
+
24
+ OPCODES = {
25
+ 'mov': 0x01,
26
+ 'push': 0x02,
27
+ 'pop': 0x03,
28
+ 'call': 0x04,
29
+ 'ret': 0x05,
30
+ 'cmp': 0x06,
31
+ 'jmp': 0x07,
32
+ 'je': 0x08,
33
+ 'jne': 0x09,
34
+ 'jg': 0x0A,
35
+ 'jl': 0x0B,
36
+ 'jge': 0x0C,
37
+ 'jle': 0x0D,
38
+ 'xor': 0x0E,
39
+ 'add': 0x0F,
40
+ 'sub': 0x10,
41
+ 'mul': 0x11,
42
+ 'div': 0x12,
43
+ 'syscall': 0x13
44
+ }
45
+
46
+ ARG_TYPE_REG = 0x01
47
+ ARG_TYPE_VAR = 0x02
48
+ ARG_TYPE_IMM = 0x03
49
+
50
+ def __init__(self):
51
+ self.variables = {}
52
+ self.labels = {}
53
+ self.bytecode = bytearray()
54
+
55
+ def compile(self, source_code: str, output_filepath: str):
56
+ data_lines, text_lines = self._split_sections(source_code)
57
+
58
+ if not text_lines:
59
+ raise AssemblyCraftCompilerError("Compilation failed: Missing or empty 'section .text'.")
60
+
61
+ self._parse_data_section(data_lines)
62
+ self._parse_text_section(text_lines)
63
+
64
+ binary_data = self._generate_binary()
65
+
66
+ try:
67
+ with open(output_filepath, "wb") as f:
68
+ f.write(binary_data)
69
+ except OSError as e:
70
+ raise AssemblyCraftCompilerError(
71
+ f"Failed to write output file '{output_filepath}': {e}"
72
+ ) from e
73
+
74
+ def _split_sections(self, code: str):
75
+ current_section = None
76
+ data_lines, text_lines = [], []
77
+
78
+ for line_num, line in enumerate(code.splitlines(), start=1):
79
+ line = re.sub(r';.*$', '', line).strip()
80
+ if not line:
81
+ continue
82
+ if line.startswith("section .data"):
83
+ current_section = "data"
84
+ continue
85
+ elif line.startswith("section .text"):
86
+ current_section = "text"
87
+ continue
88
+
89
+ if current_section == "data":
90
+ data_lines.append((line_num, line))
91
+ elif current_section == "text":
92
+ text_lines.append((line_num, line))
93
+ else:
94
+ raise AssemblyCraftCompilerError(
95
+ f"Line {line_num}: Statement '{line}' outside of any section "
96
+ f"(expected 'section .data' or 'section .text')."
97
+ )
98
+
99
+ return data_lines, text_lines
100
+
101
+ def _parse_data_section(self, lines: list):
102
+ var_id = 0
103
+ for line_num, line in lines:
104
+ match = re.match(r'^([a-zA-Z_]\w*)\s+(ds|dm|db)\s+(.+)$', line)
105
+ if not match:
106
+ raise AssemblyCraftCompilerError(
107
+ f"Line {line_num}: Malformed data statement '{line}'."
108
+ )
109
+
110
+ var_name, size_type, values_str = match.groups()
111
+
112
+ if var_name in self.variables:
113
+ raise AssemblyCraftCompilerError(
114
+ f"Line {line_num}: Duplicate variable definition '{var_name}'."
115
+ )
116
+
117
+ alloc_len = self.ALLOC_SIZES[size_type]
118
+ raw_bytes = bytearray()
119
+ tokens = re.findall(r'"([^"]*)"|(\d+|0x[0-9a-fA-F]+)', values_str)
120
+
121
+ if not tokens:
122
+ raise AssemblyCraftCompilerError(
123
+ f"Line {line_num}: No valid value initializers found for variable '{var_name}'."
124
+ )
125
+
126
+ for str_val, num_val in tokens:
127
+ if str_val:
128
+ try:
129
+ decoded_str = str_val.encode('utf-8').decode('unicode_escape')
130
+ raw_bytes.extend(decoded_str.encode('utf-8'))
131
+ except Exception as e:
132
+ raise AssemblyCraftCompilerError(
133
+ f"Line {line_num}: Invalid escape sequence in string for '{var_name}': {e}"
134
+ ) from e
135
+ elif num_val:
136
+ val = int(num_val, 0)
137
+ if not (0 <= val <= 255):
138
+ raise AssemblyCraftCompilerError(
139
+ f"Line {line_num}: Byte literal {val} out of range (0-255) in '{var_name}'."
140
+ )
141
+ raw_bytes.append(val)
142
+
143
+ if len(raw_bytes) > alloc_len:
144
+ raise AssemblyCraftCompilerError(
145
+ f"Line {line_num}: Initializer size ({len(raw_bytes)} bytes) exceeds allocated "
146
+ f"capacity for '{size_type}' ({alloc_len} bytes) in variable '{var_name}'."
147
+ )
148
+
149
+ padded_data = bytes(raw_bytes).ljust(alloc_len, b'\x00')
150
+
151
+ self.variables[var_name] = {
152
+ 'id': var_id,
153
+ 'size_type': size_type,
154
+ 'alloc_len': alloc_len,
155
+ 'data': padded_data
156
+ }
157
+ var_id += 1
158
+
159
+ def _parse_text_section(self, lines: list):
160
+ bytecode_offset = 0
161
+ cleaned_instructions = []
162
+
163
+ for line_num, line in lines:
164
+ if line.endswith(':'):
165
+ label_name = line[:-1].strip()
166
+ if not label_name or not re.match(r'^[a-zA-Z_]\w*$', label_name):
167
+ raise AssemblyCraftCompilerError(
168
+ f"Line {line_num}: Invalid label identifier '{label_name}'."
169
+ )
170
+ if label_name in self.labels:
171
+ raise AssemblyCraftCompilerError(
172
+ f"Line {line_num}: Duplicate label definition '{label_name}'."
173
+ )
174
+ self.labels[label_name] = bytecode_offset
175
+ else:
176
+ cleaned_instructions.append((line_num, line))
177
+ parts = line.split(maxsplit=1)
178
+ args_str = parts[1] if len(parts) > 1 else ""
179
+ num_args = len([a.strip() for a in args_str.split(',') if a.strip()]) if args_str else 0
180
+ bytecode_offset += 1 + (num_args * 5)
181
+
182
+ for line_num, line in cleaned_instructions:
183
+ parts = line.split(maxsplit=1)
184
+ mnemonic = parts[0].lower()
185
+ args_str = parts[1] if len(parts) > 1 else ""
186
+
187
+ if mnemonic not in self.OPCODES:
188
+ raise AssemblyCraftCompilerError(
189
+ f"Line {line_num}: Unknown instruction mnemonic '{mnemonic}'."
190
+ )
191
+
192
+ self.bytecode.append(self.OPCODES[mnemonic])
193
+
194
+ if args_str:
195
+ args = [a.strip() for a in args_str.split(',') if a.strip()]
196
+ for arg in args:
197
+ arg_type, arg_val = self._resolve_operand(arg, line_num)
198
+ self.bytecode.append(arg_type)
199
+ try:
200
+ self.bytecode += struct.pack("<i", arg_val)
201
+ except struct.error:
202
+ raise AssemblyCraftCompilerError(
203
+ f"Line {line_num}: Operand value {arg_val} exceeds 32-bit signed integer limits."
204
+ )
205
+
206
+ def _resolve_operand(self, token: str, line_num: int = None):
207
+ prefix = f"Line {line_num}: " if line_num else ""
208
+
209
+ if token in self.REGISTERS:
210
+ return self.ARG_TYPE_REG, self.REGISTERS[token]
211
+ if token in self.variables:
212
+ return self.ARG_TYPE_VAR, self.variables[token]['id']
213
+ if token in self.labels:
214
+ return self.ARG_TYPE_IMM, self.labels[token]
215
+ try:
216
+ return self.ARG_TYPE_IMM, int(token, 0)
217
+ except ValueError:
218
+ raise AssemblyCraftCompilerError(
219
+ f"{prefix}Unresolved operand or label token '{token}'."
220
+ )
221
+
222
+ def _generate_binary(self) -> bytearray:
223
+ key_table = bytearray()
224
+ data_payload = bytearray()
225
+
226
+ base_offset = 10 + (len(self.variables) * 22)
227
+ current_data_offset = base_offset
228
+
229
+ for var_name, info in self.variables.items():
230
+ encoded_name = var_name.encode('utf-8')[:16].ljust(16, b'\x00')
231
+ key_table += struct.pack("<B16sIB", info['id'], encoded_name, current_data_offset, info['alloc_len'])
232
+ data_payload += info['data']
233
+ current_data_offset += info['alloc_len']
234
+
235
+ text_offset = current_data_offset
236
+ header = bytearray(self.MAGIC_HEADER) + struct.pack("<HI", len(self.variables), text_offset)
237
+
238
+ return header + key_table + data_payload + self.bytecode
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: AssemblyCraftCompiler
3
+ Version: 1.2.0
4
+ Summary: The official Assembly Craft compiler
5
+ Author-email: Orbinuity <orbinuity@ratgers.nl>
6
+ License-Expression: BSD-3-Clause
7
+ Project-URL: Donate, https://www.patreon.com/cw/Orbinuity
8
+ Project-URL: Homepage, https://orbinuity.nl/project#ASMCC
9
+ Project-URL: Source, https://github.com/orbinuity/ASMCC
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.7
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # Assembly Craft Compiler
18
+
19
+ [![License](https://img.shields.io/github/license/Orbinuity/ASMCC)](https://orbinuity.nl/license)
20
+ [![Last Commit](https://img.shields.io/github/last-commit/Orbinuity/ASMCC)](https://github.com/Orbinuity/ASMCC/commits)
21
+ [![Version](https://img.shields.io/badge/Version-1.2.0-orange)](https://github.com/Orbinuity/ASMCC/releases/v1.2.0)
22
+ [![Made By](https://img.shields.io/badge/Made%20by-Orbinuity-teal)](https://orbinuity.nl/)
23
+ [![Read the docs](https://img.shields.io/badge/Read%20the%20docs-red)](https://help.orbinuity.nl/doc?project=ASMCC)
24
+
25
+ The official Assembly Craft compiler
26
+
27
+ ## License
28
+
29
+ Before copying any part of this project, please read the [LICENSE](https://orbinuity.nl/license) file to understand the terms and conditions.
@@ -0,0 +1,6 @@
1
+ asmcc/__init__.py,sha256=N2sIbVHN4Jdvw4I3LZk8w8PrWPgRVCXKem7fw2tXGaI,8825
2
+ assemblycraftcompiler-1.2.0.dist-info/licenses/LICENSE,sha256=5uESSdSbXo6n2Vf2g_CcPLDLLoudTw6H4KMbvJWVHpM,1458
3
+ assemblycraftcompiler-1.2.0.dist-info/METADATA,sha256=6JJyRhWNwRZlBs8nRNQzCqPkM6hDzCa8VIsuDPnInGU,1327
4
+ assemblycraftcompiler-1.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
5
+ assemblycraftcompiler-1.2.0.dist-info/top_level.txt,sha256=DjZ_MVLsCnX7-3suNs2qDoVFOkg5VsoE3DRc3MqUvIE,6
6
+ assemblycraftcompiler-1.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,11 @@
1
+ Copyright 2026 Orbinuity
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4
+
5
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6
+
7
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8
+
9
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+
11
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ asmcc