Phasor 3.1.1
Stack VM based Programming Language
Loading...
Searching...
No Matches
Serializer.py
Go to the documentation of this file.
1"""
2phasor.Serializer
3=================
4Serialises a :class:`~phasor.Bytecode.Bytecode` object to the binary ``.phsb`` format.
5"""
6
7from __future__ import annotations
8
9import struct
10import zlib
11from pathlib import Path
12from typing import Dict, List
13
14from .Bytecode import Bytecode
15from .Instruction import Instruction
16from .Metadata import (
17 HEADER_SIZE, MAGIC, SEC_CONSTANTS, SEC_FUNCTIONS,
18 SEC_INSTRUCTIONS, SEC_VARIABLES, VERSION,
19)
20from .Value import Value, ValueType
21
22
24 """Converts a :class:`~phasor.Bytecode.Bytecode` object into its binary ``.phsb`` representation."""
25
26 def __init__(self) -> None:
27 """Initialise the serializer with an empty write buffer."""
28 self._buf: bytearray = bytearray()
29
30
31 def serialize(self, bytecode: Bytecode) -> bytes:
32 """Serialise *bytecode* to the ``.phsb`` wire format.
33
34 Writes a 16-byte header (magic, version, flags, CRC-32 checksum) followed
35 by the constants, variables, functions, and instructions sections in order.
36
37 Args:
38 bytecode: The :class:`~phasor.Bytecode.Bytecode` object to serialise.
39
40 Returns:
41 The complete ``.phsb`` binary as a :class:`bytes` object.
42 """
43 self._buf = bytearray()
44
45 self._buf.extend(bytes(HEADER_SIZE))
46 data_start = len(self._buf)
47
48 self._write_constant_pool(bytecode.constants)
49 self._write_variable_mapping(bytecode.variables, bytecode.next_var_index)
50 self._write_function_entries(bytecode.function_entries)
51 self._write_instructions(bytecode.instructions)
52
53 checksum = zlib.crc32(self._buf[data_start:]) & 0xFFFFFFFF
54
55 header = struct.pack("<IIII", MAGIC, VERSION, 0, checksum)
56 self._buf[:HEADER_SIZE] = header
57
58 return bytes(self._buf)
59
60 def save_to_file(self, bytecode: Bytecode, path: Path) -> None:
61 """Serialise *bytecode* and write the result to *path*.
62
63 Parent directories are created automatically if they do not exist.
64
65 Args:
66 bytecode: The :class:`~phasor.Bytecode.Bytecode` object to serialise.
67 path: Destination file path; typically ends with ``.phsb``.
68 """
69 path = Path(path)
70 path.parent.mkdir(parents=True, exist_ok=True)
71 path.write_bytes(self.serialize(bytecode))
72
73 def _write_constant_pool(self, constants: List[Value]) -> None:
74 """Write the :data:`~phasor.Metadata.SEC_CONSTANTS` section: count followed by each :class:`~phasor.Value.Value`."""
75 self._write_uint8(SEC_CONSTANTS)
76 self._write_uint32(len(constants))
77 for value in constants:
78 self._write_value(value)
79
81 self, variables: Dict[str, int], next_var_index: int
82 ) -> None:
83 """Write the :data:`~phasor.Metadata.SEC_VARIABLES` section: count, :attr:`~phasor.Bytecode.Bytecode.next_var_index`, then each name→slot pair."""
84 self._write_uint8(SEC_VARIABLES)
85 self._write_uint32(len(variables))
86 self._write_int32(next_var_index)
87 for name, index in variables.items():
88 self._write_string(name)
89 self._write_int32(index)
90
91 def _write_function_entries(self, function_entries: Dict[str, int]) -> None:
92 """Write the :data:`~phasor.Metadata.SEC_FUNCTIONS` section: count then each name→instruction-index entry point."""
93 self._write_uint8(SEC_FUNCTIONS)
94 self._write_uint32(len(function_entries))
95 for name, address in function_entries.items():
96 self._write_string(name)
97 self._write_int32(address)
98
99 def _write_instructions(self, instructions: List[Instruction]) -> None:
100 """Write the :data:`~phasor.Metadata.SEC_INSTRUCTIONS` section: count then each :class:`~phasor.Instruction.Instruction` as ``uint8`` opcode + five ``int32`` operands."""
101 self._write_uint8(SEC_INSTRUCTIONS)
102 self._write_uint32(len(instructions))
103 for instr in instructions:
104 self._write_uint8(int(instr.op))
105 self._write_int32(instr.operand1)
106 self._write_int32(instr.operand2)
107 self._write_int32(instr.operand3)
108
109 def _write_value(self, value: Value) -> None:
110 """Write a :class:`~phasor.Value.Value` as a ``uint8`` type tag followed by its payload.
111
112 Raises:
113 NotImplementedError: If :attr:`value.type <phasor.Value.Value.type>` is not one of
114 Null, Bool, Int, Float, or String.
115 """
116 t = value.type
117 if t == ValueType.Null:
118 self._write_uint8(0)
119 elif t == ValueType.Bool:
120 self._write_uint8(1)
121 self._write_uint8(1 if value.as_bool() else 0)
122 elif t == ValueType.Int:
123 self._write_uint8(2)
124 self._write_int64(value.as_int())
125 elif t == ValueType.Float:
126 self._write_uint8(3)
127 self._write_double(value.as_float())
128 elif t == ValueType.String:
129 self._write_uint8(4)
130 self._write_string(value.as_string())
131 else:
132 raise NotImplementedError(f"Serialization not implemented for {t!r}")
133
134 def _write_uint8(self, v: int) -> None:
135 """Append a single unsigned byte to the buffer."""
136 self._buf.append(v & 0xFF)
137
138 def _write_uint16(self, v: int) -> None:
139 """Append a little-endian unsigned 16-bit integer to the buffer."""
140 self._buf.extend(struct.pack("<H", v & 0xFFFF))
141
142 def _write_uint32(self, v: int) -> None:
143 """Append a little-endian unsigned 32-bit integer to the buffer."""
144 self._buf.extend(struct.pack("<I", v & 0xFFFFFFFF))
145
146 def _write_int32(self, v: int) -> None:
147 """Append a little-endian signed 32-bit integer to the buffer."""
148 self._buf.extend(struct.pack("<i", v))
149
150 def _write_int64(self, v: int) -> None:
151 """Append a little-endian signed 64-bit integer to the buffer."""
152 self._buf.extend(struct.pack("<q", v))
153
154 def _write_double(self, v: float) -> None:
155 """Append a little-endian IEEE 754 double to the buffer."""
156 self._buf.extend(struct.pack("<d", v))
157
158 def _write_string(self, s: str) -> None:
159 """Append a length-prefixed UTF-8 string (uint16 length + bytes) to the buffer."""
160 encoded = s.encode("utf-8")
161 self._write_uint16(len(encoded))
162 self._buf.extend(encoded)
None save_to_file(self, Bytecode bytecode, Path path)
Definition Serializer.py:60
None _write_constant_pool(self, List[Value] constants)
Definition Serializer.py:73
None _write_variable_mapping(self, Dict[str, int] variables, int next_var_index)
Definition Serializer.py:82
None _write_instructions(self, List[Instruction] instructions)
Definition Serializer.py:99
None _write_value(self, Value value)
bytes serialize(self, Bytecode bytecode)
Definition Serializer.py:31
None _write_function_entries(self, Dict[str, int] function_entries)
Definition Serializer.py:91