astreum 0.1.2__tar.gz → 0.1.4__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: astreum
3
- Version: 0.1.2
3
+ Version: 0.1.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astreum"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  authors = [
5
5
  { name="Roy R. O. Okello", email="roy@stelar.xyz" },
6
6
  ]
@@ -0,0 +1,121 @@
1
+ import threading
2
+ from typing import Callable, Dict, List, Optional, Tuple
3
+ import uuid
4
+
5
+ from astreum.machine.environment import Environment
6
+ from astreum.machine.expression import Expr
7
+ from astreum.machine.tokenizer import tokenize
8
+ from astreum.machine.parser import parse
9
+
10
+ class AstreumMachine:
11
+ def __init__(self):
12
+ self.global_env = Environment()
13
+
14
+ self.sessions: Dict[str, Environment] = {}
15
+
16
+ self.lock = threading.Lock()
17
+
18
+ def create_session(self) -> str:
19
+ session_id = str(uuid.uuid4())
20
+ with self.lock:
21
+ self.sessions[session_id] = Environment(parent=self.global_env)
22
+ return session_id
23
+
24
+ def terminate_session(self, session_id: str) -> bool:
25
+ with self.lock:
26
+ if session_id in self.sessions:
27
+ del self.sessions[session_id]
28
+ return True
29
+ else:
30
+ return False
31
+
32
+ def get_session_env(self, session_id: str) -> Optional[Environment]:
33
+ with self.lock:
34
+ return self.sessions.get(session_id, None)
35
+
36
+ def evaluate_code(self, code: str, session_id: str) -> Tuple[Optional[Expr], Optional[str]]:
37
+ session_env = self.get_session_env(session_id)
38
+ if session_env is None:
39
+ return None, f"Session ID {session_id} not found."
40
+
41
+ try:
42
+ tkns = tokenize(input=code)
43
+ expr, _ = parse(tokens=tkns)
44
+ result = self.evaluate_expression(expr, session_env)
45
+ return result, None
46
+ except Exception as e:
47
+ return None, str(e)
48
+
49
+ def evaluate_expression(self, expr: Expr, env: Environment) -> Expr:
50
+ if isinstance(expr, Expr.Integer):
51
+ return expr
52
+
53
+ elif isinstance(expr, Expr.String):
54
+ return expr
55
+
56
+ elif isinstance(expr, Expr.Symbol):
57
+ value = env.get(expr.value)
58
+ if value is not None:
59
+ return value
60
+ else:
61
+ raise ValueError("Variable not found in environments.")
62
+
63
+ elif isinstance(expr, Expr.ListExpr):
64
+ if not expr.elements:
65
+ raise ValueError("Empty list cannot be evaluated.")
66
+
67
+ first = expr.elements[0]
68
+
69
+ if isinstance(first, Expr.Symbol):
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)
76
+ args = expr.elements[1:]
77
+
78
+ if len(fn_params) != len(args):
79
+ raise ValueError(f"Expected {len(fn_params)} arguments, got {len(args)}.")
80
+
81
+ # Create a new environment for the function execution, inheriting from the function's defining environment
82
+ new_env = Environment(parent=env)
83
+
84
+ # Evaluate and bind each argument
85
+ for param, arg in zip(fn_params, args):
86
+ evaluated_arg = self.evaluate_expression(arg, env)
87
+ new_env.set(param, evaluated_arg)
88
+
89
+ # Evaluate the function body within the new environment
90
+ return self.evaluate_expression(fn_body, new_env)
91
+
92
+ elif first.value in ["def", "+"]:
93
+ args = expr.elements[1:]
94
+
95
+ match first.value:
96
+ case "def":
97
+ if len(args) != 2:
98
+ raise ValueError("def expects exactly two arguments: a symbol and an expression")
99
+ if not isinstance(args[0], Expr.Symbol):
100
+ raise ValueError("First argument to def must be a symbol")
101
+
102
+ var_name = args[0].value
103
+ var_value = self.evaluate_expression(args[1], env)
104
+ env.set(var_name, var_value)
105
+ return args[0]
106
+
107
+ case "+":
108
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in args]
109
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
110
+ raise ValueError("All arguments to + must be integers")
111
+
112
+ result = sum(arg.value for arg in evaluated_args)
113
+ return Expr.Integer(result)
114
+
115
+ else:
116
+ evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
117
+ return Expr.ListExpr(evaluated_elements)
118
+ elif isinstance(expr, Expr.Function):
119
+ return expr
120
+ else:
121
+ raise ValueError(f"Unknown expression type: {type(expr)}")
@@ -1,6 +1,5 @@
1
- # Define the Environment class
2
- from typing import Callable, Dict, List, Optional
3
- from src.astreum.machine.expression import Expr
1
+ from typing import Dict, Optional
2
+ from astreum.machine.expression import Expr
4
3
 
5
4
 
6
5
  class Environment:
@@ -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:
@@ -1,7 +1,6 @@
1
- # Parser function
2
1
  from typing import List, Tuple
3
- from src.astreum.machine.error import ParseError
4
- from src.astreum.machine.expression import Expr
2
+ from astreum.machine.error import ParseError
3
+ from astreum.machine.expression import Expr
5
4
 
6
5
 
7
6
  def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
@@ -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
@@ -3,7 +3,7 @@
3
3
  # Tokenizer function
4
4
  from typing import List
5
5
 
6
- from src.astreum.machine.error import ParseError
6
+ from astreum.machine.error import ParseError
7
7
 
8
8
 
9
9
  def tokenize(input: str) -> List[str]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: astreum
3
- Version: 0.1.2
3
+ Version: 0.1.4
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
@@ -1,163 +0,0 @@
1
- import threading
2
- from typing import Callable, Dict, List, Optional, Tuple
3
- import uuid
4
-
5
- from src.astreum.machine.environment import Environment
6
- from src.astreum.machine.expression import Expr
7
- from src.astreum.machine.tokenizer import tokenize
8
- from src.astreum.machine.parser import parse
9
-
10
- class AstreumMachine:
11
- def __init__(self):
12
- # Initialize the global environment
13
- self.global_env = Environment()
14
-
15
- # Dictionary to manage user sessions: session_id -> local Environment
16
- self.sessions: Dict[str, Environment] = {}
17
-
18
- # Lock for thread-safe access to the global environment and sessions
19
- self.lock = threading.Lock()
20
-
21
- 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
- session_id = str(uuid.uuid4())
27
- with self.lock:
28
- self.sessions[session_id] = Environment(parent=self.global_env)
29
- return session_id
30
-
31
- 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
- with self.lock:
37
- if session_id in self.sessions:
38
- del self.sessions[session_id]
39
- return True
40
- else:
41
- return False
42
-
43
- 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
- with self.lock:
49
- return self.sessions.get(session_id, None)
50
-
51
- def evaluate_code(self, code: str, session_id: str) -> Tuple[Optional[Expr], Optional[str]]:
52
- """
53
- Evaluate code within the context of a user's session.
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:
60
- return None, f"Session ID {session_id} not found."
61
-
62
- try:
63
- tkns = tokenize(input=code)
64
- expr, _ = parse(tokens=tkns)
65
- result = self.evaluate(expr, env)
66
- return result, None
67
- except Exception as e:
68
- return None, str(e)
69
-
70
- def evaluate(self, expr: Expr, env: Environment) -> Expr:
71
-
72
- if isinstance(expr, Expr.Integer):
73
- return expr
74
- elif isinstance(expr, Expr.String):
75
- return expr
76
- elif isinstance(expr, Expr.Symbol):
77
- value = env.get(expr.value)
78
- if value is not None:
79
- return value
80
- else:
81
- # Return the symbol itself if not found in the environment
82
- return expr
83
- elif isinstance(expr, Expr.ListExpr):
84
- if not expr.elements:
85
- raise ValueError("Empty list cannot be evaluated")
86
-
87
- first = expr.elements[0]
88
- if isinstance(first, Expr.Symbol):
89
- # Check if it's a user-defined function
90
- user_def_fn = env.get(first.value)
91
- if isinstance(user_def_fn, Expr.Function):
92
- fn_params, fn_body = user_def_fn.params, user_def_fn.body
93
- args = expr.elements[1:]
94
-
95
- if len(fn_params) != len(args):
96
- raise TypeError(f"expected {len(fn_params)} arguments, got {len(args)}")
97
-
98
- # Create a new environment for the function execution, inheriting from the function's defining environment
99
- new_env = Environment(parent=env)
100
-
101
- # Evaluate and bind each argument
102
- for param, arg in zip(fn_params, args):
103
- evaluated_arg = self.evaluate(arg, env)
104
- new_env.set(param, evaluated_arg)
105
-
106
- # Evaluate the function body within the new environment
107
- return self.evaluate(fn_body, new_env)
108
-
109
- # Check for special functions
110
- elif first.value in ["def", "+"]:
111
- args = expr.elements[1:]
112
-
113
- match first.value:
114
- case "def":
115
- if len(args) != 2:
116
- raise TypeError("def expects exactly two arguments: a symbol and an expression")
117
- if not isinstance(args[0], Expr.Symbol):
118
- raise TypeError("First argument to def must be a symbol")
119
-
120
- var_name = args[0].value
121
- var_value = self.evaluate(args[1], env)
122
- env.set(var_name, var_value)
123
- return args[0] # Return the symbol name
124
-
125
- case "+":
126
- # Ensure all arguments are integers
127
- evaluated_args = [self.evaluate(arg, env) for arg in args]
128
- if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
129
- raise TypeError("All arguments to + must be integers")
130
-
131
- # Sum the integer values and return as an Expr.Integer
132
- result = sum(arg.value for arg in evaluated_args)
133
- return Expr.Integer(result)
134
-
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
- else:
158
- evaluated_elements = [self.evaluate(e, env) for e in expr.elements]
159
- return Expr.ListExpr(evaluated_elements)
160
- elif isinstance(expr, Expr.Function):
161
- return expr
162
- else:
163
- raise TypeError(f"Unknown expression type: {type(expr)}")
File without changes
File without changes
File without changes
File without changes