Phasor 3.1.1
Stack VM based Programming Language
Loading...
Searching...
No Matches
Bytecode.py
Go to the documentation of this file.
1"""
2phasor.Bytecode
3================
4in-memory representation of a compiled
5Phasor program.
6"""
7
8from __future__ import annotations
9
10from dataclasses import dataclass, field
11from pathlib import Path
12from typing import Dict, List, Optional
13
14from .Instruction import Instruction
15from .OpCode import OpCode
16from .Value import Value
17
18
19@dataclass
21 """In-memory representation of a compiled Phasor program.
22
23 Holds all data needed to run or serialise the program: the instruction
24 stream, the constant pool, the variable name→slot mapping, function entry
25 points, and the next free variable slot counter.
26
27 Attributes:
28 instructions: Ordered list of :class:`~phasor.Instruction.Instruction` objects forming the program.
29 constants: Constant pool; entries are indexed by :attr:`~phasor.OpCode.OpCode.PUSH_CONST` / :attr:`~phasor.OpCode.OpCode.LOAD_CONST_R`.
30 variables: Maps each variable name to its integer slot index.
31 function_entries: Maps each function name to the index of its first instruction.
32 next_var_index: Next available variable slot; serialised as part of the variables section.
33 """
34
35 instructions: List[Instruction] = field(default_factory=list)
36 constants: List[Value] = field(default_factory=list)
37 variables: Dict[str, int] = field(default_factory=dict)
38 function_entries: Dict[str, int] = field(default_factory=dict)
39 next_var_index: int = 0
40
41 def add_constant(self, value: Value) -> int:
42 """Append *value* to the constant pool and return its index."""
43 index = len(self.constants)
44 self.constants.append(value)
45 return index
46
47 def find_or_add_constant(self, value: Value) -> int:
48 """Return the index of an existing equal constant, or add a new one."""
49 try:
50 return self.constants.index(value)
51 except ValueError:
52 return self.add_constant(value)
53
54 def get_or_create_var(self, name: str) -> int:
55 """Return the slot index for *name*, creating it if absent."""
56 if name not in self.variables:
57 self.variables[name] = self.next_var_index
58 self.next_var_index += 1
59 return self.variables[name]
60
61 def emit(self, op: OpCode,
62 op1: int = 0, op2: int = 0, op3: int = 0) -> int:
63 """Append a new :class:`~phasor.Instruction.Instruction` to :attr:`instructions` and return its index.
64
65 Args:
66 op: The :class:`~phasor.OpCode.OpCode` for this instruction.
67 op1 … op5: Operand values; unused operands should be left as ``0``.
68
69 Returns:
70 The zero-based index of the newly appended instruction.
71 """
72 self.instructions.append(Instruction(op, op1, op2, op3))
73 return len(self.instructions) - 1
74
75 def patch_operand1(self, instr_index: int, value: int) -> None:
76 """Overwrite ``operand1`` of the instruction at *instr_index* in-place.
77
78 Typically used to back-patch forward-jump offsets after the jump target
79 is known.
80
81 Args:
82 instr_index: Index into :attr:`instructions` of the instruction to patch.
83 value: New value for ``operand1``.
84 """
85 self.instructions[instr_index].operand1 = value
86
87 def save(self, path: Path | str) -> None:
88 """Serialise this object and write it to a ``.phsb`` file at *path*.
89
90 Delegates to :class:`~phasor.Serializer.BytecodeSerializer`.
91 """
92 from .Serializer import BytecodeSerializer
93 BytecodeSerializer().save_to_file(self, Path(path))
94
95 @classmethod
96 def load(cls, path: Path | str) -> "Bytecode":
97 """Read a ``.phsb`` file at *path* and return a deserialised :class:`Bytecode`.
98
99 Delegates to :class:`~phasor.Deserializer.BytecodeDeserializer`.
100 """
101 from .Deserializer import BytecodeDeserializer
102 return BytecodeDeserializer().load_from_file(Path(path))
103
104 @classmethod
105 def from_bytes(cls, data: bytes | bytearray) -> "Bytecode":
106 """Deserialise a :class:`Bytecode` from a raw ``.phsb`` byte buffer.
107
108 Delegates to :class:`~phasor.Deserializer.BytecodeDeserializer`.
109 """
110 from .Deserializer import BytecodeDeserializer
111 return BytecodeDeserializer().deserialize(bytes(data))
112
113 def to_bytes(self) -> bytes:
114 """Serialise this object to a raw ``.phsb`` byte buffer.
115
116 Delegates to :class:`~phasor.Serializer.BytecodeSerializer`.
117 """
118 from .Deserializer import BytecodeSerializer
119 return bytes(BytecodeSerializer().serialize(self))
120
121 @classmethod
122 def from_native_binary(cls, path: Path | str) -> "Bytecode":
123 """Extract and deserialise bytecode from an ELF/PE/MachO binary's ``.phsb`` section."""
124 from .Native import extract_phsb_bytes
125 raw = extract_phsb_bytes(Path(path))
126 return cls.from_bytes(raw)
127
128 def disassemble(self) -> str:
129 """Return a human-readable disassembly of :attr:`instructions`.
130
131 Function entry points from :attr:`function_entries` are printed as
132 ``<function name>:`` labels above their first instruction.
133 """
134 lines: List[str] = []
135 for i, instr in enumerate(self.instructions):
136 for name, addr in self.function_entries.items():
137 if addr == i:
138 lines.append(f"\n<function {name}>:")
139 ops = [instr.operand1, instr.operand2, instr.operand3,
140 instr.operand4, instr.operand5]
141 non_zero = [str(o) for o in ops if o != 0]
142 operands = (" " + ", ".join(non_zero)) if non_zero else ""
143 lines.append(f" {i:>4} {instr.op.name:<20}{operands}")
144 return "\n".join(lines)
145
146 def __repr__(self) -> str:
147 """Return a summary showing instruction, constant, variable, and function counts."""
148 return (
149 f"Bytecode("
150 f"{len(self.instructions)} instructions, "
151 f"{len(self.constants)} constants, "
152 f"{len(self.variables)} variables, "
153 f"{len(self.function_entries)} functions)"
154 )
int find_or_add_constant(self, Value value)
Definition Bytecode.py:47
None patch_operand1(self, int instr_index, int value)
Definition Bytecode.py:75
"Bytecode" from_bytes(cls, bytes|bytearray data)
Definition Bytecode.py:105
"Bytecode" from_native_binary(cls, Path|str path)
Definition Bytecode.py:122
"Bytecode" load(cls, Path|str path)
Definition Bytecode.py:96
int add_constant(self, Value value)
Definition Bytecode.py:41
None save(self, Path|str path)
Definition Bytecode.py:87
int emit(self, OpCode op, int op1=0, int op2=0, int op3=0)
Definition Bytecode.py:62
int get_or_create_var(self, str name)
Definition Bytecode.py:54