astreum 0.1.2__py3-none-any.whl → 0.1.3__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.
Potentially problematic release.
This version of astreum might be problematic. Click here for more details.
- astreum/machine/__init__.py +27 -69
- astreum/machine/environment.py +1 -4
- astreum/machine/parser.py +0 -4
- {astreum-0.1.2.dist-info → astreum-0.1.3.dist-info}/METADATA +1 -1
- astreum-0.1.3.dist-info/RECORD +12 -0
- astreum-0.1.2.dist-info/RECORD +0 -12
- {astreum-0.1.2.dist-info → astreum-0.1.3.dist-info}/LICENSE +0 -0
- {astreum-0.1.2.dist-info → astreum-0.1.3.dist-info}/WHEEL +0 -0
- {astreum-0.1.2.dist-info → astreum-0.1.3.dist-info}/top_level.txt +0 -0
astreum/machine/__init__.py
CHANGED
|
@@ -9,30 +9,19 @@ from src.astreum.machine.parser import parse
|
|
|
9
9
|
|
|
10
10
|
class AstreumMachine:
|
|
11
11
|
def __init__(self):
|
|
12
|
-
# Initialize the global environment
|
|
13
12
|
self.global_env = Environment()
|
|
14
13
|
|
|
15
|
-
# Dictionary to manage user sessions: session_id -> local Environment
|
|
16
14
|
self.sessions: Dict[str, Environment] = {}
|
|
17
15
|
|
|
18
|
-
# Lock for thread-safe access to the global environment and sessions
|
|
19
16
|
self.lock = threading.Lock()
|
|
20
17
|
|
|
21
18
|
def create_session(self) -> str:
|
|
22
|
-
"""
|
|
23
|
-
Create a new user session with a unique session ID and a fresh local environment.
|
|
24
|
-
Returns the session ID.
|
|
25
|
-
"""
|
|
26
19
|
session_id = str(uuid.uuid4())
|
|
27
20
|
with self.lock:
|
|
28
21
|
self.sessions[session_id] = Environment(parent=self.global_env)
|
|
29
22
|
return session_id
|
|
30
23
|
|
|
31
24
|
def terminate_session(self, session_id: str) -> bool:
|
|
32
|
-
"""
|
|
33
|
-
Terminate an existing user session.
|
|
34
|
-
Returns True if the session was successfully terminated, False otherwise.
|
|
35
|
-
"""
|
|
36
25
|
with self.lock:
|
|
37
26
|
if session_id in self.sessions:
|
|
38
27
|
del self.sessions[session_id]
|
|
@@ -41,123 +30,92 @@ class AstreumMachine:
|
|
|
41
30
|
return False
|
|
42
31
|
|
|
43
32
|
def get_session_env(self, session_id: str) -> Optional[Environment]:
|
|
44
|
-
"""
|
|
45
|
-
Retrieve the local environment for a given session ID.
|
|
46
|
-
Returns the Environment if found, None otherwise.
|
|
47
|
-
"""
|
|
48
33
|
with self.lock:
|
|
49
34
|
return self.sessions.get(session_id, None)
|
|
50
35
|
|
|
51
36
|
def evaluate_code(self, code: str, session_id: str) -> Tuple[Optional[Expr], Optional[str]]:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Returns a tuple of (Result Expression, Error Message).
|
|
55
|
-
If evaluation is successful, Error Message is None.
|
|
56
|
-
If an error occurs, Result Expression is None.
|
|
57
|
-
"""
|
|
58
|
-
env = self.get_session_env(session_id)
|
|
59
|
-
if env is None:
|
|
37
|
+
session_env = self.get_session_env(session_id)
|
|
38
|
+
if session_env is None:
|
|
60
39
|
return None, f"Session ID {session_id} not found."
|
|
61
40
|
|
|
62
41
|
try:
|
|
63
42
|
tkns = tokenize(input=code)
|
|
64
43
|
expr, _ = parse(tokens=tkns)
|
|
65
|
-
result = self.
|
|
44
|
+
result = self.evaluate_expression(expr, session_env)
|
|
66
45
|
return result, None
|
|
67
46
|
except Exception as e:
|
|
68
47
|
return None, str(e)
|
|
69
48
|
|
|
70
|
-
def
|
|
71
|
-
|
|
49
|
+
def evaluate_expression(self, expr: Expr, env: Environment) -> Expr:
|
|
72
50
|
if isinstance(expr, Expr.Integer):
|
|
73
51
|
return expr
|
|
52
|
+
|
|
74
53
|
elif isinstance(expr, Expr.String):
|
|
75
54
|
return expr
|
|
55
|
+
|
|
76
56
|
elif isinstance(expr, Expr.Symbol):
|
|
77
57
|
value = env.get(expr.value)
|
|
78
58
|
if value is not None:
|
|
79
59
|
return value
|
|
80
60
|
else:
|
|
81
|
-
|
|
82
|
-
|
|
61
|
+
raise ValueError("Variable not found in environments.")
|
|
62
|
+
|
|
83
63
|
elif isinstance(expr, Expr.ListExpr):
|
|
84
64
|
if not expr.elements:
|
|
85
|
-
raise ValueError("Empty list cannot be evaluated")
|
|
65
|
+
raise ValueError("Empty list cannot be evaluated.")
|
|
86
66
|
|
|
87
67
|
first = expr.elements[0]
|
|
68
|
+
|
|
88
69
|
if isinstance(first, Expr.Symbol):
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
|
|
71
|
+
first_symbol_value = env.get(first.value)
|
|
72
|
+
|
|
73
|
+
if first_symbol_value and not isinstance(first_symbol_value, Expr.Function):
|
|
74
|
+
evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
|
|
75
|
+
return Expr.ListExpr(evaluated_elements)
|
|
93
76
|
args = expr.elements[1:]
|
|
94
77
|
|
|
95
78
|
if len(fn_params) != len(args):
|
|
96
|
-
raise
|
|
79
|
+
raise ValueError(f"Expected {len(fn_params)} arguments, got {len(args)}.")
|
|
97
80
|
|
|
98
81
|
# Create a new environment for the function execution, inheriting from the function's defining environment
|
|
99
82
|
new_env = Environment(parent=env)
|
|
100
83
|
|
|
101
84
|
# Evaluate and bind each argument
|
|
102
85
|
for param, arg in zip(fn_params, args):
|
|
103
|
-
evaluated_arg = self.
|
|
86
|
+
evaluated_arg = self.evaluate_expression(arg, env)
|
|
104
87
|
new_env.set(param, evaluated_arg)
|
|
105
88
|
|
|
106
89
|
# Evaluate the function body within the new environment
|
|
107
|
-
return self.
|
|
90
|
+
return self.evaluate_expression(fn_body, new_env)
|
|
108
91
|
|
|
109
|
-
# Check for special functions
|
|
110
92
|
elif first.value in ["def", "+"]:
|
|
111
93
|
args = expr.elements[1:]
|
|
112
94
|
|
|
113
95
|
match first.value:
|
|
114
96
|
case "def":
|
|
115
97
|
if len(args) != 2:
|
|
116
|
-
raise
|
|
98
|
+
raise ValueError("def expects exactly two arguments: a symbol and an expression")
|
|
117
99
|
if not isinstance(args[0], Expr.Symbol):
|
|
118
|
-
raise
|
|
100
|
+
raise ValueError("First argument to def must be a symbol")
|
|
119
101
|
|
|
120
102
|
var_name = args[0].value
|
|
121
|
-
var_value = self.
|
|
103
|
+
var_value = self.evaluate_expression(args[1], env)
|
|
122
104
|
env.set(var_name, var_value)
|
|
123
|
-
return args[0]
|
|
105
|
+
return args[0]
|
|
124
106
|
|
|
125
107
|
case "+":
|
|
126
|
-
|
|
127
|
-
evaluated_args = [self.evaluate(arg, env) for arg in args]
|
|
108
|
+
evaluated_args = [self.evaluate_expression(arg, env) for arg in args]
|
|
128
109
|
if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
|
|
129
|
-
raise
|
|
110
|
+
raise ValueError("All arguments to + must be integers")
|
|
130
111
|
|
|
131
|
-
# Sum the integer values and return as an Expr.Integer
|
|
132
112
|
result = sum(arg.value for arg in evaluated_args)
|
|
133
113
|
return Expr.Integer(result)
|
|
134
114
|
|
|
135
|
-
else:
|
|
136
|
-
# Attempt to evaluate as a function application
|
|
137
|
-
func = self.evaluate(first, env)
|
|
138
|
-
if isinstance(func, Expr.Function):
|
|
139
|
-
fn_params, fn_body = func.params, func.body
|
|
140
|
-
args = expr.elements[1:]
|
|
141
|
-
|
|
142
|
-
if len(fn_params) != len(args):
|
|
143
|
-
raise TypeError(f"expected {len(fn_params)} arguments, got {len(args)}")
|
|
144
|
-
|
|
145
|
-
# Create a new environment for the function execution, inheriting from the function's defining environment
|
|
146
|
-
new_env = Environment(parent=func.env)
|
|
147
|
-
|
|
148
|
-
# Evaluate and bind each argument
|
|
149
|
-
for param, arg in zip(fn_params, args):
|
|
150
|
-
evaluated_arg = self.evaluate(arg, env)
|
|
151
|
-
new_env.set(param, evaluated_arg)
|
|
152
|
-
|
|
153
|
-
# Evaluate the function body within the new environment
|
|
154
|
-
return self.evaluate(fn_body, new_env)
|
|
155
|
-
else:
|
|
156
|
-
raise TypeError(f"'{first.value}' is not a function")
|
|
157
115
|
else:
|
|
158
|
-
evaluated_elements = [self.
|
|
116
|
+
evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
|
|
159
117
|
return Expr.ListExpr(evaluated_elements)
|
|
160
118
|
elif isinstance(expr, Expr.Function):
|
|
161
119
|
return expr
|
|
162
120
|
else:
|
|
163
|
-
raise
|
|
121
|
+
raise ValueError(f"Unknown expression type: {type(expr)}")
|
astreum/machine/environment.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
from typing import Callable, Dict, List, Optional
|
|
1
|
+
from typing import Dict, Optional
|
|
3
2
|
from src.astreum.machine.expression import Expr
|
|
4
3
|
|
|
5
4
|
|
|
@@ -9,11 +8,9 @@ class Environment:
|
|
|
9
8
|
self.parent = parent
|
|
10
9
|
|
|
11
10
|
def set(self, name: str, value: Expr):
|
|
12
|
-
"""Set a variable in the current environment."""
|
|
13
11
|
self.data[name] = value
|
|
14
12
|
|
|
15
13
|
def get(self, name: str) -> Optional[Expr]:
|
|
16
|
-
"""Retrieve a variable's value, searching parent environments if necessary."""
|
|
17
14
|
if name in self.data:
|
|
18
15
|
return self.data[name]
|
|
19
16
|
elif self.parent:
|
astreum/machine/parser.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# Parser function
|
|
2
1
|
from typing import List, Tuple
|
|
3
2
|
from src.astreum.machine.error import ParseError
|
|
4
3
|
from src.astreum.machine.expression import Expr
|
|
@@ -19,7 +18,6 @@ def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
|
|
|
19
18
|
|
|
20
19
|
while inner_tokens:
|
|
21
20
|
if inner_tokens[0] == ')':
|
|
22
|
-
# End of list
|
|
23
21
|
return Expr.ListExpr(list_items), inner_tokens[1:]
|
|
24
22
|
|
|
25
23
|
expr, inner_tokens = parse(inner_tokens)
|
|
@@ -35,10 +33,8 @@ def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
|
|
|
35
33
|
return Expr.String(string_content), rest
|
|
36
34
|
|
|
37
35
|
else:
|
|
38
|
-
# Try to parse as integer
|
|
39
36
|
try:
|
|
40
37
|
number = int(first_token)
|
|
41
38
|
return Expr.Integer(number), rest
|
|
42
39
|
except ValueError:
|
|
43
|
-
# Treat as symbol
|
|
44
40
|
return Expr.Symbol(first_token), rest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: astreum
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Python library to interact with the Astreum blockchain and its Lispeum virtual machine.
|
|
5
5
|
Author-email: "Roy R. O. Okello" <roy@stelar.xyz>
|
|
6
6
|
Project-URL: Homepage, https://github.com/astreum/lib
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
astreum/__init__.py,sha256=-hmy95qFWlCbmHEcj5sGniM-UtpIn-iwwhVWFrFkd_w,37
|
|
2
|
+
astreum/machine/__init__.py,sha256=xitq5kFvtcBY88w3yEfsV3r75IIwBhoX6x0jx7as2Fw,5240
|
|
3
|
+
astreum/machine/environment.py,sha256=F1zXHTZpfX4GazUbPhgh43j8EdfV5bedS-b4xWBB_NY,611
|
|
4
|
+
astreum/machine/error.py,sha256=MvqBaZZt33rNELNhUJ2lER3TE3aS8WVqsWF2hz2AwoA,38
|
|
5
|
+
astreum/machine/expression.py,sha256=yOovmkiE11IEIjqEuYwuG7-jx6msOpgyzl3C3yD331c,1298
|
|
6
|
+
astreum/machine/parser.py,sha256=5tSAVi5z7laEYZHsoDofEOq4KKTfo5md6epT2Bsgk8o,1199
|
|
7
|
+
astreum/machine/tokenizer.py,sha256=4QzdZ11ocZKxlcjvV54PUxDuZxuL4et_sz3ILwfp3ko,1441
|
|
8
|
+
astreum-0.1.3.dist-info/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
|
|
9
|
+
astreum-0.1.3.dist-info/METADATA,sha256=RoW6ek_WGNmQzA_n5oHrY7YHS6Z2hMJQeF1OoMOL7z4,740
|
|
10
|
+
astreum-0.1.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
11
|
+
astreum-0.1.3.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
|
|
12
|
+
astreum-0.1.3.dist-info/RECORD,,
|
astreum-0.1.2.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
astreum/__init__.py,sha256=-hmy95qFWlCbmHEcj5sGniM-UtpIn-iwwhVWFrFkd_w,37
|
|
2
|
-
astreum/machine/__init__.py,sha256=H0phu-6FDw3ByJfm8Tm3ewq0MVNHoJhEVeGkqwDZTc8,7481
|
|
3
|
-
astreum/machine/environment.py,sha256=vL6vL4INfWslgK4KfNFvqH__0t25HRJRptrEQM1aofo,805
|
|
4
|
-
astreum/machine/error.py,sha256=MvqBaZZt33rNELNhUJ2lER3TE3aS8WVqsWF2hz2AwoA,38
|
|
5
|
-
astreum/machine/expression.py,sha256=yOovmkiE11IEIjqEuYwuG7-jx6msOpgyzl3C3yD331c,1298
|
|
6
|
-
astreum/machine/parser.py,sha256=k8gPsQppV-61_n-bmOBekIE-6sO1xMYudJBYlwlVw8k,1315
|
|
7
|
-
astreum/machine/tokenizer.py,sha256=4QzdZ11ocZKxlcjvV54PUxDuZxuL4et_sz3ILwfp3ko,1441
|
|
8
|
-
astreum-0.1.2.dist-info/LICENSE,sha256=gYBvRDP-cPLmTyJhvZ346QkrYW_eleke4Z2Yyyu43eQ,1089
|
|
9
|
-
astreum-0.1.2.dist-info/METADATA,sha256=BGnpbctoLafXbPjio-V6i7Bf-3JktwDtokeSeDc1OUA,740
|
|
10
|
-
astreum-0.1.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
11
|
-
astreum-0.1.2.dist-info/top_level.txt,sha256=1EG1GmkOk3NPmUA98FZNdKouhRyget-KiFiMk0i2Uz0,8
|
|
12
|
-
astreum-0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|