astreum 0.2.41__py3-none-any.whl → 0.2.61__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.
@@ -1,177 +1,236 @@
1
- from typing import List, Union
1
+ from typing import List, Optional, Union
2
2
  import uuid
3
3
 
4
4
  from .environment import Env
5
- from .expression import Expr
5
+ from .expression import Expr, error_expr, ERROR_SYMBOL
6
6
  from .meter import Meter
7
-
8
-
9
- def high_eval(self, env_id: uuid.UUID, expr: Expr, meter = None) -> Expr:
10
-
11
- if meter is None:
12
- meter = Meter()
13
-
14
- # ---------- atoms ----------
15
- if isinstance(expr, Expr.Error):
16
- return expr
17
-
18
- if isinstance(expr, Expr.Symbol):
19
- bound = self.env_get(env_id, expr.value.encode())
20
- if bound is None:
21
- return Expr.Error(f"unbound symbol '{expr.value}'", origin=expr)
22
- return bound
23
-
24
- if not isinstance(expr, Expr.ListExpr):
25
- return expr # Expr.Byte or other literals passthrough
26
-
27
- # ---------- empty / single ----------
28
- if len(expr.elements) == 0:
29
- return expr
30
- if len(expr.elements) == 1:
31
- return self.high_eval(env_id=env_id, expr=expr.elements[0], meter=meter)
32
-
33
- tail = expr.elements[-1]
34
-
35
- # ---------- (value name def) ----------
36
- if isinstance(tail, Expr.Symbol) and tail.value == "def":
37
- if len(expr.elements) < 3:
38
- return Expr.Error("def expects (value name def)", origin=expr)
39
- name_e = expr.elements[-2]
40
- if not isinstance(name_e, Expr.Symbol):
41
- return Expr.Error("def name must be symbol", origin=name_e)
42
- value_e = expr.elements[-3]
43
- value_res = self.high_eval(env_id=env_id, expr=value_e, meter=meter)
44
- if isinstance(value_res, Expr.Error):
45
- return value_res
46
- self.env_set(env_id, name_e.value.encode(), value_res)
47
- return value_res
48
-
49
- # ---- LOW-LEVEL call: ( arg1 arg2 ... ( (body) sk ) ) ----
50
- if isinstance(tail, Expr.ListExpr):
51
- inner = tail.elements
52
- if len(inner) >= 2 and isinstance(inner[-1], Expr.Symbol) and inner[-1].value == "sk":
53
- body_expr = inner[-2]
54
- if not isinstance(body_expr, Expr.ListExpr):
55
- return Expr.Error("sk body must be list", origin=body_expr)
56
-
57
- # helper: turn an Expr into a contiguous bytes buffer
58
- def to_bytes(v: Expr) -> Union[bytes, Expr.Error]:
59
- if isinstance(v, Expr.Byte):
60
- return bytes([v.value & 0xFF])
61
- if isinstance(v, Expr.ListExpr):
62
- # expect a list of Expr.Byte
63
- out: bytearray = bytearray()
64
- for el in v.elements:
65
- if isinstance(el, Expr.Byte):
66
- out.append(el.value & 0xFF)
67
- else:
68
- return Expr.Error("byte list must contain only Byte", origin=el)
69
- return bytes(out)
70
- if isinstance(v, Expr.Error):
71
- return v
72
- return Expr.Error("argument must resolve to Byte or (Byte ...)", origin=v)
73
-
74
- # resolve ALL preceding args into bytes (can be Byte or List[Byte])
75
- args_exprs = expr.elements[:-1]
76
- arg_bytes: List[bytes] = []
77
- for a in args_exprs:
78
- v = self.high_eval(env_id=env_id, expr=a, meter=meter)
79
- if isinstance(v, Expr.Error):
80
- return v
81
- vb = to_bytes(v)
82
- if isinstance(vb, Expr.Error):
83
- return vb
84
- arg_bytes.append(vb)
85
-
86
- # build low-level code with $0-based placeholders ($0 = first arg)
87
- code: List[bytes] = []
88
-
89
- def emit(tok: Expr) -> Union[None, Expr.Error]:
90
- if isinstance(tok, Expr.Symbol):
91
- name = tok.value
92
- if name.startswith("$"):
93
- idx_s = name[1:]
94
- if not idx_s.isdigit():
95
- return Expr.Error("invalid sk placeholder", origin=tok)
96
- idx = int(idx_s) # $0 is first
97
- if idx < 0 or idx >= len(arg_bytes):
98
- return Expr.Error("arity mismatch in sk placeholder", origin=tok)
99
- code.append(arg_bytes[idx])
100
- return None
101
- code.append(name.encode())
102
- return None
103
-
104
- if isinstance(tok, Expr.Byte):
105
- code.append(bytes([tok.value & 0xFF]))
106
- return None
107
-
108
- if isinstance(tok, Expr.ListExpr):
109
- rv = self.high_eval(env_id, tok, meter=meter)
110
- if isinstance(rv, Expr.Error):
111
- return rv
112
- rb = to_bytes(rv)
113
- if isinstance(rb, Expr.Error):
114
- return rb
115
- code.append(rb)
116
- return None
117
-
118
- if isinstance(tok, Expr.Error):
119
- return tok
120
-
121
- return Expr.Error("invalid token in sk body", origin=tok)
122
-
123
- for t in body_expr.elements:
124
- err = emit(t)
125
- if isinstance(err, Expr.Error):
126
- return err
127
-
128
- # Execute low-level code built from sk-body using the caller's meter
129
- res = self.low_eval(code, meter=meter)
130
- return res
131
-
132
- # ---------- (... (body params fn)) HIGH-LEVEL CALL ----------
133
- if isinstance(tail, Expr.ListExpr):
134
- fn_form = tail
135
- if (len(fn_form.elements) >= 3
136
- and isinstance(fn_form.elements[-1], Expr.Symbol)
137
- and fn_form.elements[-1].value == "fn"):
138
-
139
- body_expr = fn_form.elements[-3]
140
- params_expr = fn_form.elements[-2]
141
-
142
- if not isinstance(body_expr, Expr.ListExpr):
143
- return Expr.Error("fn body must be list", origin=body_expr)
144
- if not isinstance(params_expr, Expr.ListExpr):
145
- return Expr.Error("fn params must be list", origin=params_expr)
146
-
147
- params: List[bytes] = []
148
- for p in params_expr.elements:
149
- if not isinstance(p, Expr.Symbol):
150
- return Expr.Error("fn param must be symbol", origin=p)
151
- params.append(p.value.encode())
152
-
153
- args_exprs = expr.elements[:-1]
154
- if len(args_exprs) != len(params):
155
- return Expr.Error("arity mismatch", origin=expr)
156
-
157
- arg_bytes: List[bytes] = []
158
- for a in args_exprs:
159
- v = self.high_eval(env_id, a, meter=meter)
160
- if isinstance(v, Expr.Error):
161
- return v
162
- if not isinstance(v, Expr.Byte):
163
- return Expr.Error("argument must resolve to Byte", origin=a)
164
- arg_bytes.append(bytes([v.value & 0xFF]))
165
-
166
- # child env, bind params -> Expr.Byte
167
- child_env = uuid.uuid4()
168
- self.environments[child_env] = Env(parent_id=env_id)
169
- for name_b, val_b in zip(params, arg_bytes):
170
- self.env_set(child_env, name_b, Expr.Byte(val_b[0]))
171
-
172
- # evaluate HL body, metered from the top
173
- return self.high_eval(child_env, body_expr, meter=meter)
174
-
175
- # ---------- default: resolve each element and return list ----------
176
- resolved: List[Expr] = [self.high_eval(env_id, e, meter=meter) for e in expr.elements]
177
- return Expr.ListExpr(resolved)
7
+
8
+
9
+ def _is_error(expr: Expr) -> bool:
10
+ return (
11
+ isinstance(expr, Expr.ListExpr)
12
+ and bool(expr.elements)
13
+ and isinstance(expr.elements[0], Expr.Symbol)
14
+ and expr.elements[0].value == ERROR_SYMBOL
15
+ )
16
+
17
+
18
+ def _hex_symbol_to_bytes(value: Optional[str]) -> Optional[bytes]:
19
+ if not value:
20
+ return None
21
+ data = value.strip()
22
+ if data.startswith(("0x", "0X")):
23
+ data = data[2:]
24
+ if len(data) % 2:
25
+ data = "0" + data
26
+ try:
27
+ return bytes.fromhex(data)
28
+ except ValueError:
29
+ return None
30
+
31
+
32
+ def _expr_to_bytes(expr: Expr) -> Optional[bytes]:
33
+ if isinstance(expr, Expr.Bytes):
34
+ return expr.value
35
+ if isinstance(expr, Expr.Symbol):
36
+ return _hex_symbol_to_bytes(expr.value)
37
+ return None
38
+
39
+
40
+ def high_eval(self, env_id: uuid.UUID, expr: Expr, meter = None) -> Expr:
41
+ if meter is None:
42
+ meter = Meter()
43
+
44
+ call_env_id = uuid.uuid4()
45
+ self.environments[call_env_id] = Env(parent_id=env_id)
46
+ env_id = call_env_id
47
+
48
+ try:
49
+ # ---------- atoms ----------
50
+ if _is_error(expr):
51
+ return expr
52
+
53
+ if isinstance(expr, Expr.Symbol):
54
+ bound = self.env_get(env_id, expr.value.encode())
55
+ if bound is None:
56
+ return error_expr("eval", f"unbound symbol '{expr.value}'")
57
+ return bound
58
+
59
+ if not isinstance(expr, Expr.ListExpr):
60
+ return expr # Expr.Byte or other literals passthrough
61
+
62
+ # ---------- empty / single ----------
63
+ if len(expr.elements) == 0:
64
+ return expr
65
+ if len(expr.elements) == 1:
66
+ return self.high_eval(env_id, expr.elements[0], meter)
67
+
68
+ tail = expr.elements[-1]
69
+
70
+ # ---------- (value name def) ----------
71
+ if isinstance(tail, Expr.Symbol) and tail.value == "def":
72
+ if len(expr.elements) < 3:
73
+ return error_expr("eval", "def expects (value name def)")
74
+ name_e = expr.elements[-2]
75
+ if not isinstance(name_e, Expr.Symbol):
76
+ return error_expr("eval", "def name must be symbol")
77
+ value_e = expr.elements[-3]
78
+ value_res = self.high_eval(env_id, value_e, meter)
79
+ if _is_error(value_res):
80
+ return value_res
81
+ self.env_set(env_id, name_e.value.encode(), value_res)
82
+ return value_res
83
+
84
+ # Reference Call
85
+ # (atom_id ref)
86
+ if isinstance(tail, Expr.Symbol) and tail.value == "ref":
87
+ if len(expr.elements) != 2:
88
+ return error_expr("eval", "ref expects (atom_id ref)")
89
+ key_bytes = _expr_to_bytes(expr.elements[0])
90
+ if not key_bytes:
91
+ return error_expr("eval", "ref expects (atom_id ref)")
92
+ stored_list = self.get_expr_list_from_storage(key_bytes)
93
+ if stored_list is None:
94
+ return error_expr("eval", "ref target not found")
95
+ return stored_list
96
+
97
+ # Low Level Call
98
+ # (arg1 arg2 ... ((body) sk))
99
+ if isinstance(tail, Expr.ListExpr):
100
+ inner = tail.elements
101
+ if len(inner) >= 2 and isinstance(inner[-1], Expr.Symbol) and inner[-1].value == "sk":
102
+ body_expr = inner[-2]
103
+ if not isinstance(body_expr, Expr.ListExpr):
104
+ return error_expr("eval", "sk body must be list")
105
+
106
+ # helper: turn an Expr into a contiguous bytes buffer
107
+ def to_bytes(v: Expr) -> Union[bytes, Expr]:
108
+ if isinstance(v, Expr.Byte):
109
+ return bytes([v.value & 0xFF])
110
+ if isinstance(v, Expr.ListExpr):
111
+ # expect a list of Expr.Byte
112
+ out: bytearray = bytearray()
113
+ for el in v.elements:
114
+ if isinstance(el, Expr.Byte):
115
+ out.append(el.value & 0xFF)
116
+ else:
117
+ return error_expr("eval", "byte list must contain only Byte elements")
118
+ return bytes(out)
119
+ if _is_error(v):
120
+ return v
121
+ return error_expr("eval", "argument must resolve to Byte or (Byte ...)")
122
+
123
+ # resolve ALL preceding args into bytes (can be Byte or List[Byte])
124
+ args_exprs = expr.elements[:-1]
125
+ arg_bytes: List[bytes] = []
126
+ for a in args_exprs:
127
+ v = self.high_eval(env_id, a, meter)
128
+ if _is_error(v):
129
+ return v
130
+ vb = to_bytes(v)
131
+ if not isinstance(vb, bytes):
132
+ if _is_error(vb):
133
+ return vb
134
+ return error_expr("eval", "unexpected expression while coercing to bytes")
135
+ arg_bytes.append(vb)
136
+
137
+ # build low-level code with $0-based placeholders ($0 = first arg)
138
+ code: List[bytes] = []
139
+
140
+ def emit(tok: Expr) -> Union[None, Expr]:
141
+ if isinstance(tok, Expr.Symbol):
142
+ name = tok.value
143
+ if name.startswith("$"):
144
+ idx_s = name[1:]
145
+ if not idx_s.isdigit():
146
+ return error_expr("eval", "invalid sk placeholder")
147
+ idx = int(idx_s) # $0 is first
148
+ if idx < 0 or idx >= len(arg_bytes):
149
+ return error_expr("eval", "arity mismatch in sk placeholder")
150
+ code.append(arg_bytes[idx])
151
+ return None
152
+ code.append(name.encode())
153
+ return None
154
+
155
+ if isinstance(tok, Expr.Byte):
156
+ code.append(bytes([tok.value & 0xFF]))
157
+ return None
158
+
159
+ if isinstance(tok, Expr.ListExpr):
160
+ rv = self.high_eval(env_id, tok, meter)
161
+ if _is_error(rv):
162
+ return rv
163
+ rb = to_bytes(rv)
164
+ if not isinstance(rb, bytes):
165
+ if _is_error(rb):
166
+ return rb
167
+ return error_expr("eval", "unexpected expression while coercing list token to bytes")
168
+ code.append(rb)
169
+ return None
170
+
171
+ if _is_error(tok):
172
+ return tok
173
+
174
+ return error_expr("eval", "invalid token in sk body")
175
+
176
+ for t in body_expr.elements:
177
+ err = emit(t)
178
+ if err is not None and _is_error(err):
179
+ return err
180
+
181
+ # Execute low-level code built from sk-body using the caller's meter
182
+ res = self.low_eval(code, meter=meter)
183
+ return res
184
+
185
+ # High Level Call
186
+ # (arg1 arg2 ... ((body) (params) fn))
187
+ if isinstance(tail, Expr.ListExpr):
188
+ fn_form = tail
189
+ if (len(fn_form.elements) >= 3
190
+ and isinstance(fn_form.elements[-1], Expr.Symbol)
191
+ and fn_form.elements[-1].value == "fn"):
192
+
193
+ body_expr = fn_form.elements[-3]
194
+ params_expr = fn_form.elements[-2]
195
+
196
+ if not isinstance(body_expr, Expr.ListExpr):
197
+ return error_expr("eval", "fn body must be list")
198
+ if not isinstance(params_expr, Expr.ListExpr):
199
+ return error_expr("eval", "fn params must be list")
200
+
201
+ params: List[bytes] = []
202
+ for p in params_expr.elements:
203
+ if not isinstance(p, Expr.Symbol):
204
+ return error_expr("eval", "fn param must be symbol")
205
+ params.append(p.value.encode())
206
+
207
+ args_exprs = expr.elements[:-1]
208
+ if len(args_exprs) != len(params):
209
+ return error_expr("eval", "arity mismatch")
210
+
211
+ arg_bytes: List[bytes] = []
212
+ for a in args_exprs:
213
+ v = self.high_eval(env_id, a, meter)
214
+ if _is_error(v):
215
+ return v
216
+ if not isinstance(v, Expr.Byte):
217
+ return error_expr("eval", "argument must resolve to Byte")
218
+ arg_bytes.append(bytes([v.value & 0xFF]))
219
+
220
+ # child env, bind params -> Expr.Byte
221
+ child_env = uuid.uuid4()
222
+ self.environments[child_env] = Env(parent_id=env_id)
223
+ try:
224
+ for name_b, val_b in zip(params, arg_bytes):
225
+ self.env_set(child_env, name_b, Expr.Byte(val_b[0]))
226
+
227
+ # evaluate HL body, metered from the top
228
+ return self.high_eval(child_env, body_expr, meter)
229
+ finally:
230
+ self.environments.pop(child_env, None)
231
+
232
+ # ---------- default: resolve each element and return list ----------
233
+ resolved: List[Expr] = [self.high_eval(env_id, e, meter) for e in expr.elements]
234
+ return Expr.ListExpr(resolved)
235
+ finally:
236
+ self.environments.pop(call_env_id, None)
@@ -1,6 +1,6 @@
1
- from typing import Dict, List, Union
2
- from .expression import Expr
3
- from .meter import Meter
1
+ from typing import Dict, List, Union
2
+ from .expression import Expr, error_expr
3
+ from .meter import Meter
4
4
 
5
5
  def tc_to_int(b: bytes) -> int:
6
6
  """bytes -> int using two's complement (width = len(b)*8)."""
@@ -35,19 +35,19 @@ def nand_bytes(a: bytes, b: bytes) -> bytes:
35
35
  resu = (~(au & bu)) & mask
36
36
  return resu.to_bytes(w, "big", signed=False)
37
37
 
38
- def low_eval(self, code: List[bytes], meter: Meter) -> Expr:
38
+ def low_eval(self, code: List[bytes], meter: Meter) -> Expr:
39
39
 
40
40
  heap: Dict[bytes, bytes] = {}
41
41
 
42
42
  stack: List[bytes] = []
43
43
  pc = 0
44
44
 
45
- while True:
46
- if pc >= len(code):
47
- if len(stack) != 1:
48
- return Expr.Error("bad stack")
49
- # wrap successful result as an Expr.Bytes
50
- return Expr.Bytes(stack.pop())
45
+ while True:
46
+ if pc >= len(code):
47
+ if len(stack) != 1:
48
+ return error_expr("low_eval", "bad stack")
49
+ # wrap successful result as an Expr.Bytes
50
+ return Expr.Bytes(stack.pop())
51
51
 
52
52
  tok = code[pc]
53
53
  pc += 1
@@ -55,7 +55,7 @@ def low_eval(self, code: List[bytes], meter: Meter) -> Expr:
55
55
  # ---------- ADD ----------
56
56
  if tok == b"add":
57
57
  if len(stack) < 2:
58
- return Expr.Error("underflow")
58
+ return error_expr("low_eval", "underflow")
59
59
  b_b = stack.pop()
60
60
  a_b = stack.pop()
61
61
  a_i = tc_to_int(a_b)
@@ -65,56 +65,56 @@ def low_eval(self, code: List[bytes], meter: Meter) -> Expr:
65
65
  res_b = int_to_tc(res_i, width)
66
66
  # charge for both operands' byte widths
67
67
  if not meter.charge_bytes(len(a_b) + len(b_b)):
68
- return Expr.Error("meter limit")
68
+ return error_expr("low_eval", "meter limit")
69
69
  stack.append(res_b)
70
70
  continue
71
71
 
72
72
  # ---------- NAND ----------
73
73
  if tok == b"nand":
74
74
  if len(stack) < 2:
75
- return Expr.Error("underflow")
75
+ return error_expr("low_eval", "underflow")
76
76
  b_b = stack.pop()
77
77
  a_b = stack.pop()
78
78
  res_b = nand_bytes(a_b, b_b)
79
79
  # bitwise cost: 2 * max(len(a), len(b))
80
80
  if not meter.charge_bytes(2 * max(len(a_b), len(b_b), 1)):
81
- return Expr.Error("meter limit")
81
+ return error_expr("low_eval", "meter limit")
82
82
  stack.append(res_b)
83
83
  continue
84
84
 
85
85
  # ---------- JUMP ----------
86
86
  if tok == b"jump":
87
87
  if len(stack) < 1:
88
- return Expr.Error("underflow")
88
+ return error_expr("low_eval", "underflow")
89
89
  tgt_b = stack.pop()
90
90
  if not meter.charge_bytes(1):
91
- return Expr.Error("meter limit")
91
+ return error_expr("low_eval", "meter limit")
92
92
  tgt_i = tc_to_int(tgt_b)
93
93
  if tgt_i < 0 or tgt_i >= len(code):
94
- return Expr.Error("bad jump")
94
+ return error_expr("low_eval", "bad jump")
95
95
  pc = tgt_i
96
96
  continue
97
97
 
98
98
  # ---------- HEAP GET ----------
99
99
  if tok == b"heap_get":
100
100
  if len(stack) < 1:
101
- return Expr.Error("underflow")
101
+ return error_expr("low_eval", "underflow")
102
102
  key = stack.pop()
103
103
  val = heap.get(key) or b""
104
104
  # get cost: 1
105
105
  if not meter.charge_bytes(1):
106
- return Expr.Error("meter limit")
106
+ return error_expr("low_eval", "meter limit")
107
107
  stack.append(val)
108
108
  continue
109
109
 
110
110
  # ---------- HEAP SET ----------
111
111
  if tok == b"heap_set":
112
112
  if len(stack) < 2:
113
- return Expr.Error("underflow")
113
+ return error_expr("low_eval", "underflow")
114
114
  val = stack.pop()
115
115
  key = stack.pop()
116
116
  if not meter.charge_bytes(len(val)):
117
- return Expr.Error("meter limit")
117
+ return error_expr("low_eval", "meter limit")
118
118
  heap[key] = val
119
119
  continue
120
120
 
@@ -1,5 +1,5 @@
1
- from typing import List, Tuple
2
- from src.astreum._lispeum import Expr
1
+ from typing import List, Tuple
2
+ from . import Expr
3
3
 
4
4
  class ParseError(Exception):
5
5
  pass
@@ -14,12 +14,7 @@ def _parse_one(tokens: List[str], pos: int = 0) -> Tuple[Expr, int]:
14
14
  i = pos + 1
15
15
  while i < len(tokens):
16
16
  if tokens[i] == ')':
17
- # special-case error form at close: (origin topic err) or (topic err)
18
- if len(items) >= 3 and isinstance(items[-1], Expr.Symbol) and items[-1].value == 'err' and isinstance(items[-2], Expr.Symbol):
19
- return Expr.Error(items[-2].value, origin=items[-3]), i + 1
20
- if len(items) == 2 and isinstance(items[-1], Expr.Symbol) and items[-1].value == 'err' and isinstance(items[-2], Expr.Symbol):
21
- return Expr.Error(items[-2].value), i + 1
22
- return Expr.ListExpr(items), i + 1
17
+ return Expr.ListExpr(items), i + 1
23
18
  expr, i = _parse_one(tokens, i)
24
19
  items.append(expr)
25
20
  raise ParseError("expected ')'")
@@ -27,30 +22,30 @@ def _parse_one(tokens: List[str], pos: int = 0) -> Tuple[Expr, int]:
27
22
  if tok == ')':
28
23
  raise ParseError("unexpected ')'")
29
24
 
30
- # try integer → Bytes (variable-length two's complement)
31
- try:
32
- n = int(tok)
33
- # encode as minimal-width signed two's complement, big-endian
34
- def int_to_min_tc(v: int) -> bytes:
35
- """Return the minimal-width signed two's complement big-endian
36
- byte encoding of integer v. Width expands just enough so that
37
- decoding with signed=True yields the same value and sign.
38
- Example: 0 -> b"\x00", 127 -> b"\x7f", 128 -> b"\x00\x80".
39
- """
40
- if v == 0:
41
- return b"\x00"
42
- w = 1
43
- while True:
44
- try:
45
- return v.to_bytes(w, "big", signed=True)
46
- except OverflowError:
47
- w += 1
48
-
49
- return Expr.Bytes(int_to_min_tc(n)), pos + 1
50
- except ValueError:
51
- return Expr.Symbol(tok), pos + 1
25
+ # try integer → Bytes (variable-length two's complement)
26
+ try:
27
+ n = int(tok)
28
+ # encode as minimal-width signed two's complement, big-endian
29
+ def int_to_min_tc(v: int) -> bytes:
30
+ """Return the minimal-width signed two's complement big-endian
31
+ byte encoding of integer v. Width expands just enough so that
32
+ decoding with signed=True yields the same value and sign.
33
+ Example: 0 -> b"\x00", 127 -> b"\x7f", 128 -> b"\x00\x80".
34
+ """
35
+ if v == 0:
36
+ return b"\x00"
37
+ w = 1
38
+ while True:
39
+ try:
40
+ return v.to_bytes(w, "big", signed=True)
41
+ except OverflowError:
42
+ w += 1
43
+
44
+ return Expr.Bytes(int_to_min_tc(n)), pos + 1
45
+ except ValueError:
46
+ return Expr.Symbol(tok), pos + 1
52
47
 
53
48
  def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
54
49
  """Parse tokens into an Expr and return (expr, remaining_tokens)."""
55
50
  expr, next_pos = _parse_one(tokens, 0)
56
- return expr, tokens[next_pos:]
51
+ return expr, tokens[next_pos:]