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.
- astreum/_communication/message.py +1 -0
- astreum/_communication/peer.py +23 -11
- astreum/_communication/route.py +40 -3
- astreum/_communication/setup.py +72 -4
- astreum/_consensus/account.py +62 -137
- astreum/_consensus/accounts.py +7 -36
- astreum/_consensus/block.py +311 -328
- astreum/_consensus/genesis.py +15 -84
- astreum/_consensus/receipt.py +67 -108
- astreum/_consensus/setup.py +50 -3
- astreum/_consensus/transaction.py +141 -118
- astreum/_consensus/workers/validation.py +5 -2
- astreum/_consensus/workers/verify.py +1 -1
- astreum/_lispeum/expression.py +190 -37
- astreum/_lispeum/high_evaluation.py +232 -173
- astreum/_lispeum/low_evaluation.py +21 -21
- astreum/_lispeum/parser.py +26 -31
- astreum/_node.py +154 -14
- astreum/_storage/__init__.py +7 -5
- astreum/_storage/atom.py +88 -96
- astreum/_storage/patricia.py +51 -16
- astreum/_storage/setup.py +35 -0
- astreum/models/block.py +20 -20
- astreum/utils/bytes.py +24 -0
- astreum/utils/integer.py +25 -0
- astreum/utils/logging.py +219 -0
- {astreum-0.2.41.dist-info → astreum-0.2.61.dist-info}/METADATA +14 -1
- astreum-0.2.61.dist-info/RECORD +57 -0
- astreum-0.2.41.dist-info/RECORD +0 -53
- {astreum-0.2.41.dist-info → astreum-0.2.61.dist-info}/WHEEL +0 -0
- {astreum-0.2.41.dist-info → astreum-0.2.61.dist-info}/licenses/LICENSE +0 -0
- {astreum-0.2.41.dist-info → astreum-0.2.61.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
if
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if isinstance(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
88
|
+
return error_expr("low_eval", "underflow")
|
|
89
89
|
tgt_b = stack.pop()
|
|
90
90
|
if not meter.charge_bytes(1):
|
|
91
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
117
|
+
return error_expr("low_eval", "meter limit")
|
|
118
118
|
heap[key] = val
|
|
119
119
|
continue
|
|
120
120
|
|
astreum/_lispeum/parser.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import List, Tuple
|
|
2
|
-
from
|
|
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
|
-
|
|
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:]
|