astreum 0.1.3__tar.gz → 0.1.5__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.

Files changed (33) hide show
  1. {astreum-0.1.3 → astreum-0.1.5}/PKG-INFO +2 -2
  2. {astreum-0.1.3 → astreum-0.1.5}/README.md +1 -1
  3. {astreum-0.1.3 → astreum-0.1.5}/pyproject.toml +1 -1
  4. astreum-0.1.5/src/astreum/lispeum/__init__.py +0 -0
  5. astreum-0.1.5/src/astreum/lispeum/expression.py +95 -0
  6. {astreum-0.1.3/src/astreum/machine → astreum-0.1.5/src/astreum/lispeum}/parser.py +2 -2
  7. astreum-0.1.5/src/astreum/lispeum/special/__init__.py +0 -0
  8. astreum-0.1.5/src/astreum/lispeum/special/definition.py +27 -0
  9. astreum-0.1.5/src/astreum/lispeum/special/list/__init__.py +0 -0
  10. astreum-0.1.5/src/astreum/lispeum/special/list/all.py +32 -0
  11. astreum-0.1.5/src/astreum/lispeum/special/list/any.py +32 -0
  12. astreum-0.1.5/src/astreum/lispeum/special/list/fold.py +29 -0
  13. astreum-0.1.5/src/astreum/lispeum/special/list/get.py +20 -0
  14. astreum-0.1.5/src/astreum/lispeum/special/list/insert.py +23 -0
  15. astreum-0.1.5/src/astreum/lispeum/special/list/map.py +30 -0
  16. astreum-0.1.5/src/astreum/lispeum/special/list/position.py +33 -0
  17. astreum-0.1.5/src/astreum/lispeum/special/list/remove.py +22 -0
  18. astreum-0.1.5/src/astreum/lispeum/special/number/__init__.py +0 -0
  19. astreum-0.1.5/src/astreum/lispeum/special/number/addition.py +0 -0
  20. {astreum-0.1.3/src/astreum/machine → astreum-0.1.5/src/astreum/lispeum}/tokenizer.py +1 -1
  21. astreum-0.1.5/src/astreum/machine/__init__.py +255 -0
  22. {astreum-0.1.3 → astreum-0.1.5}/src/astreum/machine/environment.py +1 -1
  23. {astreum-0.1.3 → astreum-0.1.5}/src/astreum.egg-info/PKG-INFO +2 -2
  24. astreum-0.1.5/src/astreum.egg-info/SOURCES.txt +28 -0
  25. astreum-0.1.3/src/astreum/machine/__init__.py +0 -121
  26. astreum-0.1.3/src/astreum/machine/expression.py +0 -50
  27. astreum-0.1.3/src/astreum.egg-info/SOURCES.txt +0 -14
  28. {astreum-0.1.3 → astreum-0.1.5}/LICENSE +0 -0
  29. {astreum-0.1.3 → astreum-0.1.5}/setup.cfg +0 -0
  30. {astreum-0.1.3 → astreum-0.1.5}/src/astreum/__init__.py +0 -0
  31. {astreum-0.1.3 → astreum-0.1.5}/src/astreum/machine/error.py +0 -0
  32. {astreum-0.1.3 → astreum-0.1.5}/src/astreum.egg-info/dependency_links.txt +0 -0
  33. {astreum-0.1.3 → astreum-0.1.5}/src/astreum.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: astreum
3
- Version: 0.1.3
3
+ Version: 0.1.5
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
@@ -20,5 +20,5 @@ Python library to interact with the Astreum blockchain and its Lispeum virtual m
20
20
 
21
21
  ## Testing
22
22
 
23
- python -m unittest discover -s tests
23
+ python3 -m unittest discover -s tests
24
24
 
@@ -6,5 +6,5 @@ Python library to interact with the Astreum blockchain and its Lispeum virtual m
6
6
 
7
7
  ## Testing
8
8
 
9
- python -m unittest discover -s tests
9
+ python3 -m unittest discover -s tests
10
10
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astreum"
3
- version = "0.1.3"
3
+ version = "0.1.5"
4
4
  authors = [
5
5
  { name="Roy R. O. Okello", email="roy@stelar.xyz" },
6
6
  ]
File without changes
@@ -0,0 +1,95 @@
1
+ from typing import List, Union
2
+
3
+ class Expr:
4
+ class ListExpr:
5
+ def __init__(self, elements: List['Expr']):
6
+ self.elements = elements
7
+
8
+ def __eq__(self, other):
9
+ if not isinstance(other, Expr.ListExpr):
10
+ return NotImplemented
11
+ return self.elements == other.elements
12
+
13
+ def __ne__(self, other):
14
+ return not self.__eq__(other)
15
+
16
+ @property
17
+ def value(self):
18
+ inner = " ".join(str(e) for e in self.elements)
19
+ return f"({inner})"
20
+
21
+
22
+ def __repr__(self):
23
+ if not self.elements:
24
+ return "()"
25
+
26
+ inner = " ".join(str(e) for e in self.elements)
27
+ return f"({inner})"
28
+
29
+ def __iter__(self):
30
+ return iter(self.elements)
31
+
32
+ def __getitem__(self, index: Union[int, slice]):
33
+ return self.elements[index]
34
+
35
+ def __len__(self):
36
+ return len(self.elements)
37
+
38
+ class Symbol:
39
+ def __init__(self, value: str):
40
+ self.value = value
41
+
42
+ def __repr__(self):
43
+ return self.value
44
+
45
+ class Integer:
46
+ def __init__(self, value: int):
47
+ self.value = value
48
+
49
+ def __repr__(self):
50
+ return str(self.value)
51
+
52
+ class String:
53
+ def __init__(self, value: str):
54
+ self.value = value
55
+
56
+ def __repr__(self):
57
+ return f'"{self.value}"'
58
+
59
+ class Boolean:
60
+ def __init__(self, value: bool):
61
+ self.value = value
62
+
63
+ def __repr__(self):
64
+ return "true" if self.value else "false"
65
+
66
+ class Function:
67
+ def __init__(self, params: List[str], body: 'Expr'):
68
+ self.params = params
69
+ self.body = body
70
+
71
+ def __repr__(self):
72
+ params_str = " ".join(self.params)
73
+ body_str = str(self.body)
74
+ return f"(fn ({params_str}) {body_str})"
75
+
76
+ class Error:
77
+ """
78
+ Represents an error with a freeform category and message.
79
+ - `category`: A string identifying the type of error (e.g., 'SyntaxError').
80
+ - `message`: A human-readable description of the error.
81
+ - `details`: Optional, additional information about the error.
82
+ """
83
+ def __init__(self, category: str, message: str, details: str = None):
84
+ if not category:
85
+ raise ValueError("Error category must be provided.")
86
+ if not message:
87
+ raise ValueError("Error message must be provided.")
88
+ self.category = category
89
+ self.message = message
90
+ self.details = details
91
+
92
+ def __repr__(self):
93
+ if self.details:
94
+ return f'({self.category} "{self.message}" {self.details})'
95
+ return f'({self.category} "{self.message}")'
@@ -1,6 +1,6 @@
1
1
  from typing import List, Tuple
2
- from src.astreum.machine.error import ParseError
3
- from src.astreum.machine.expression import Expr
2
+ from astreum.machine.error import ParseError
3
+ from astreum.lispeum.expression import Expr
4
4
 
5
5
 
6
6
  def parse(tokens: List[str]) -> Tuple[Expr, List[str]]:
File without changes
@@ -0,0 +1,27 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+
5
+ def handle_definition(machine, args: Expr.ListExpr, env: Environment) -> Expr:
6
+ if len(args) != 2:
7
+ return Expr.Error(
8
+ category="SyntaxError",
9
+ message="def expects exactly two arguments: a symbol and an expression"
10
+ )
11
+
12
+ if not isinstance(args[0], Expr.Symbol):
13
+ return Expr.Error(
14
+ category="TypeError",
15
+ message="First argument to def must be a symbol"
16
+ )
17
+
18
+ var_name = args[0].value
19
+
20
+ result = machine.evaluate_expression(args[1], env)
21
+
22
+ if isinstance(result, Expr.Error):
23
+ return result
24
+
25
+ env.set(var_name, result)
26
+
27
+ return result
@@ -0,0 +1,32 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+ def handle_list_all(machine, list: Expr.ListExpr, predicate: Expr.Function, env: Environment) -> Expr:
5
+ if not isinstance(list, Expr.ListExpr):
6
+ return Expr.ListExpr([
7
+ Expr.ListExpr([]),
8
+ Expr.String("First argument must be a list")
9
+ ])
10
+
11
+ if not isinstance(predicate, Expr.Function):
12
+ return Expr.ListExpr([
13
+ Expr.ListExpr([]),
14
+ Expr.String("Second argument must be a function")
15
+ ])
16
+
17
+ for elem in list.elements:
18
+ new_env = Environment(parent=env)
19
+ new_env.set(predicate.params[0], elem)
20
+
21
+ result, _ = machine.evaluate_expression(predicate, new_env)
22
+
23
+ if result == Expr.Boolean(False):
24
+ return Expr.ListExpr([
25
+ Expr.Boolean(False),
26
+ Expr.ListExpr([])
27
+ ])
28
+
29
+ return Expr.ListExpr([
30
+ Expr.Boolean(True),
31
+ Expr.ListExpr([])
32
+ ])
@@ -0,0 +1,32 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+ def handle_list_any(machine, list: Expr.ListExpr, predicate: Expr.Function, env: Environment) -> Expr:
5
+ if not isinstance(list, Expr.ListExpr):
6
+ return Expr.ListExpr([
7
+ Expr.ListExpr([]),
8
+ Expr.String("First argument must be a list")
9
+ ])
10
+
11
+ if not isinstance(predicate, Expr.Function):
12
+ return Expr.ListExpr([
13
+ Expr.ListExpr([]),
14
+ Expr.String("Second argument must be a function")
15
+ ])
16
+
17
+ for elem in list.elements:
18
+ new_env = Environment(parent=env)
19
+ new_env.set(predicate.params[0], elem)
20
+
21
+ result, _ = machine.evaluate_expression(predicate, new_env)
22
+
23
+ if result == Expr.Boolean(True):
24
+ return Expr.ListExpr([
25
+ Expr.Boolean(True),
26
+ Expr.ListExpr([])
27
+ ])
28
+
29
+ return Expr.ListExpr([
30
+ Expr.Boolean(False),
31
+ Expr.ListExpr([])
32
+ ])
@@ -0,0 +1,29 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+
5
+ def handle_list_fold(machine, list: Expr.ListExpr, initial: Expr, func: Expr.Function, env: Environment) -> Expr:
6
+ if not isinstance(list, Expr.ListExpr):
7
+ return Expr.ListExpr([
8
+ Expr.ListExpr([]),
9
+ Expr.String("First argument must be a list")
10
+ ])
11
+
12
+ if not isinstance(func, Expr.Function):
13
+ return Expr.ListExpr([
14
+ Expr.ListExpr([]),
15
+ Expr.String("Third argument must be a function")
16
+ ])
17
+
18
+ result = initial
19
+ for elem in list.elements:
20
+ new_env = Environment(parent=env)
21
+ new_env.set(func.params[0], result)
22
+ new_env.set(func.params[1], elem)
23
+
24
+ result = machine.evaluate_expression(func, new_env)
25
+
26
+ return Expr.ListExpr([
27
+ result,
28
+ Expr.ListExpr([])
29
+ ])
@@ -0,0 +1,20 @@
1
+
2
+
3
+ from astreum.lispeum.expression import Expr
4
+ from astreum.machine.environment import Environment
5
+
6
+
7
+ def handle_list_get(machine, list: Expr.ListExpr, index: Expr.Integer, env: Environment) -> Expr:
8
+ if not isinstance(list, Expr.ListExpr):
9
+ return Expr.ListExpr([
10
+ Expr.ListExpr([]),
11
+ Expr.String("First argument must be a list")
12
+ ])
13
+
14
+ if index.value < 0 or index.value >= len(list.elements):
15
+ return Expr.ListExpr([
16
+ Expr.ListExpr([]),
17
+ Expr.String("Index out of bounds")
18
+ ])
19
+
20
+ return machine.evaluate_expression(list.elements[index])
@@ -0,0 +1,23 @@
1
+ from astreum.lispeum.expression import Expr
2
+
3
+
4
+ def handle_list_insert(list: Expr.ListExpr, index: Expr.Integer, value: Expr) -> Expr:
5
+ if not isinstance(list, Expr.ListExpr):
6
+ return Expr.ListExpr([
7
+ Expr.ListExpr([]),
8
+ Expr.String("First argument must be a list")
9
+ ])
10
+
11
+ if index.value < 0 or index.value > len(list.elements):
12
+ return Expr.ListExpr([
13
+ Expr.ListExpr([]),
14
+ Expr.String("Index out of bounds")
15
+ ])
16
+
17
+ new_elements = list.elements[:index.value] + [value] + list.elements[index.value:]
18
+
19
+ return Expr.ListExpr([
20
+ Expr.ListExpr(new_elements),
21
+ Expr.ListExpr([])
22
+ ])
23
+
@@ -0,0 +1,30 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+
5
+ def handle_list_map(machine, list: Expr.ListExpr, func: Expr.Function, env: Environment) -> Expr:
6
+ if not isinstance(list, Expr.ListExpr):
7
+ return Expr.ListExpr([
8
+ Expr.ListExpr([]),
9
+ Expr.String("First argument must be a list")
10
+ ])
11
+
12
+ if not isinstance(func, Expr.Function):
13
+ return Expr.ListExpr([
14
+ Expr.ListExpr([]),
15
+ Expr.String("Second argument must be a function")
16
+ ])
17
+
18
+ mapped_elements = []
19
+ for elem in list.elements:
20
+ new_env = Environment(parent=env)
21
+ new_env.set(func.params[0], elem)
22
+
23
+ mapped_elem = machine.evaluate_expression(func.body, new_env)
24
+
25
+ mapped_elements.append(mapped_elem)
26
+
27
+ return Expr.ListExpr([
28
+ Expr.ListExpr(mapped_elements),
29
+ Expr.ListExpr([])
30
+ ])
@@ -0,0 +1,33 @@
1
+ from astreum.lispeum.expression import Expr
2
+ from astreum.machine.environment import Environment
3
+
4
+
5
+ def handle_list_position(machine, list: Expr.ListExpr, predicate: Expr.Function, env: Environment) -> Expr:
6
+ if not isinstance(list, Expr.ListExpr):
7
+ return Expr.ListExpr([
8
+ Expr.ListExpr([]),
9
+ Expr.String("First argument must be a list")
10
+ ])
11
+
12
+ if not isinstance(predicate, Expr.Function):
13
+ return Expr.ListExpr([
14
+ Expr.ListExpr([]),
15
+ Expr.String("Second argument must be a function")
16
+ ])
17
+
18
+ for idx, elem in enumerate(list.elements):
19
+ new_env = Environment(parent=env)
20
+ new_env.set(predicate.params[0], elem)
21
+
22
+ result, _ = machine.evaluate_expression(predicate, new_env)
23
+
24
+ if result == Expr.Boolean(True):
25
+ return Expr.ListExpr([
26
+ Expr.Integer(idx),
27
+ Expr.ListExpr([])
28
+ ])
29
+
30
+ return Expr.ListExpr([
31
+ Expr.ListExpr([]),
32
+ Expr.ListExpr([])
33
+ ])
@@ -0,0 +1,22 @@
1
+ from astreum.lispeum.expression import Expr
2
+
3
+
4
+ def handle_list_remove(list: Expr.ListExpr, index: Expr.Integer) -> Expr:
5
+ if not isinstance(list, Expr.ListExpr):
6
+ return Expr.ListExpr([
7
+ Expr.ListExpr([]),
8
+ Expr.String("First argument must be a list")
9
+ ])
10
+
11
+ if index.value < 0 or index.value >= len(list.elements):
12
+ return Expr.ListExpr([
13
+ Expr.ListExpr([]),
14
+ Expr.String("Index out of bounds")
15
+ ])
16
+
17
+ new_elements = list.elements[:index.value] + list.elements[index.value + 1:]
18
+
19
+ return Expr.ListExpr([
20
+ Expr.ListExpr(new_elements),
21
+ Expr.ListExpr([])
22
+ ])
@@ -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]:
@@ -0,0 +1,255 @@
1
+ import threading
2
+ from typing import Dict, Optional
3
+ import uuid
4
+
5
+ from astreum.lispeum.special.definition import handle_definition
6
+ from astreum.lispeum.special.list.all import handle_list_all
7
+ from astreum.lispeum.special.list.any import handle_list_any
8
+ from astreum.lispeum.special.list.fold import handle_list_fold
9
+ from astreum.lispeum.special.list.get import handle_list_get
10
+ from astreum.lispeum.special.list.insert import handle_list_insert
11
+ from astreum.lispeum.special.list.map import handle_list_map
12
+ from astreum.lispeum.special.list.position import handle_list_position
13
+ from astreum.lispeum.special.list.remove import handle_list_remove
14
+ from astreum.machine.environment import Environment
15
+ from astreum.lispeum.expression import Expr
16
+ from astreum.lispeum.tokenizer import tokenize
17
+ from astreum.lispeum.parser import parse
18
+
19
+ class AstreumMachine:
20
+ def __init__(self):
21
+ self.global_env = Environment()
22
+
23
+ self.sessions: Dict[str, Environment] = {}
24
+
25
+ self.lock = threading.Lock()
26
+
27
+ def create_session(self) -> str:
28
+ session_id = str(uuid.uuid4())
29
+ with self.lock:
30
+ self.sessions[session_id] = Environment(parent=self.global_env)
31
+ return session_id
32
+
33
+ def terminate_session(self, session_id: str) -> bool:
34
+ with self.lock:
35
+ if session_id in self.sessions:
36
+ del self.sessions[session_id]
37
+ return True
38
+ else:
39
+ return False
40
+
41
+ def get_session_env(self, session_id: str) -> Optional[Environment]:
42
+ with self.lock:
43
+ return self.sessions.get(session_id, None)
44
+
45
+ def evaluate_code(self, code: str, session_id: str) -> Expr:
46
+ session_env = self.get_session_env(session_id)
47
+ if session_env is None:
48
+ raise ValueError(f"Session ID {session_id} not found.")
49
+
50
+ try:
51
+ tkns = tokenize(input=code)
52
+ expr, _ = parse(tokens=tkns)
53
+ result = self.evaluate_expression(expr, session_env)
54
+ return result
55
+
56
+ except Exception as e:
57
+ raise ValueError(e)
58
+
59
+ def evaluate_expression(self, expr: Expr, env: Environment) -> Expr:
60
+ if isinstance(expr, Expr.Boolean) or isinstance(expr, Expr.Integer) or isinstance(expr, Expr.String) or isinstance(expr, Expr.Error):
61
+ return expr
62
+
63
+ elif isinstance(expr, Expr.Symbol):
64
+ value = env.get(expr.value)
65
+
66
+ if value:
67
+ return value
68
+ else:
69
+ return Expr.Error(
70
+ category="NameError",
71
+ message=f"Variable '{expr.value}' not found in environments."
72
+ )
73
+
74
+ elif isinstance(expr, Expr.ListExpr):
75
+
76
+ if len(expr.elements) == 0:
77
+ return expr
78
+
79
+ if len(expr.elements) == 1:
80
+ return self.evaluate_expression(expr=expr.elements[0], env=env)
81
+
82
+ first = expr.elements[0]
83
+
84
+ if isinstance(first, Expr.Symbol):
85
+
86
+ first_symbol_value = env.get(first.value)
87
+
88
+ if first_symbol_value and not isinstance(first_symbol_value, Expr.Function):
89
+ evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
90
+ return Expr.ListExpr(evaluated_elements)
91
+
92
+ elif first.value == "def":
93
+ return handle_definition(
94
+ machine=self,
95
+ args=expr.elements[1:],
96
+ env=env
97
+ )
98
+
99
+ # List
100
+ elif first.value == "list.new":
101
+ return Expr.ListExpr([self.evaluate_expression(arg, env) for arg in expr.elements[1:]])
102
+
103
+ elif first.value == "list.get":
104
+ args = expr.elements[1:]
105
+ if len(args) != 2:
106
+ return Expr.Error(
107
+ category="SyntaxError",
108
+ message="list.get expects exactly two arguments: a list and an index"
109
+ )
110
+ list_obj = self.evaluate_expression(args[0], env)
111
+ index = self.evaluate_expression(args[1], env)
112
+ return handle_list_get(self, list_obj, index, env)
113
+
114
+ elif first.value == "list.insert":
115
+ args = expr.elements[1:]
116
+ if len(args) != 3:
117
+ return Expr.ListExpr([
118
+ Expr.ListExpr([]),
119
+ Expr.String("list.insert expects exactly three arguments: a list, an index, and a value")
120
+ ])
121
+
122
+ return handle_list_insert(
123
+ list=self.evaluate_expression(args[0], env),
124
+ index=self.evaluate_expression(args[1], env),
125
+ value=self.evaluate_expression(args[2], env),
126
+ )
127
+
128
+ elif first.value == "list.remove":
129
+ args = expr.elements[1:]
130
+ if len(args) != 2:
131
+ return Expr.ListExpr([
132
+ Expr.ListExpr([]),
133
+ Expr.String("list.remove expects exactly two arguments: a list and an index")
134
+ ])
135
+
136
+ return handle_list_remove(
137
+ list=self.evaluate_expression(args[0], env),
138
+ index=self.evaluate_expression(args[1], env),
139
+ )
140
+
141
+ elif first.value == "list.length":
142
+ args = expr.elements[1:]
143
+ if len(args) != 1:
144
+ return Expr.ListExpr([
145
+ Expr.ListExpr([]),
146
+ Expr.String("list.length expects exactly one argument: a list")
147
+ ])
148
+
149
+ list_obj = self.evaluate_expression(args[0], env)
150
+ if not isinstance(list_obj, Expr.ListExpr):
151
+ return Expr.ListExpr([
152
+ Expr.ListExpr([]),
153
+ Expr.String("Argument must be a list")
154
+ ])
155
+
156
+ return Expr.ListExpr([
157
+ Expr.Integer(len(list_obj.elements)),
158
+ Expr.ListExpr([])
159
+ ])
160
+
161
+ elif first.value == "list.fold":
162
+ if len(args) != 3:
163
+ return Expr.ListExpr([
164
+ Expr.ListExpr([]),
165
+ Expr.String("list.fold expects exactly three arguments: a list, an initial value, and a function")
166
+ ])
167
+
168
+ return handle_list_fold(
169
+ machine=self,
170
+ list=self.evaluate_expression(args[0], env),
171
+ initial=self.evaluate_expression(args[1], env),
172
+ func=self.evaluate_expression(args[2], env),
173
+ env=env,
174
+ )
175
+
176
+ elif first.value == "list.map":
177
+ if len(args) != 2:
178
+ return Expr.ListExpr([
179
+ Expr.ListExpr([]),
180
+ Expr.String("list.map expects exactly two arguments: a list and a function")
181
+ ])
182
+
183
+ return handle_list_map(
184
+ machine=self,
185
+ list=self.evaluate_expression(args[0], env),
186
+ func=self.evaluate_expression(args[1], env),
187
+ env=env,
188
+ )
189
+
190
+ elif first.value == "list.position":
191
+ if len(args) != 2:
192
+ return Expr.ListExpr([
193
+ Expr.ListExpr([]),
194
+ Expr.String("list.position expects exactly two arguments: a list and a function")
195
+ ])
196
+
197
+ return handle_list_position(
198
+ machine=self,
199
+ list=self.evaluate_expression(args[0], env),
200
+ predicate=self.evaluate_expression(args[1], env),
201
+ env=env,
202
+ )
203
+
204
+ elif first.value == "list.any":
205
+ if len(args) != 2:
206
+ return Expr.ListExpr([
207
+ Expr.ListExpr([]),
208
+ Expr.String("list.any expects exactly two arguments: a list and a function")
209
+ ])
210
+
211
+ return handle_list_any(
212
+ machine=self,
213
+ list=self.evaluate_expression(args[0], env),
214
+ predicate=self.evaluate_expression(args[1], env),
215
+ env=env,
216
+ )
217
+
218
+ elif first.value == "list.all":
219
+ if len(args) != 2:
220
+ return Expr.ListExpr([
221
+ Expr.ListExpr([]),
222
+ Expr.String("list.all expects exactly two arguments: a list and a function")
223
+ ])
224
+
225
+ return handle_list_all(
226
+ machine=self,
227
+ list=self.evaluate_expression(args[0], env),
228
+ predicate=self.evaluate_expression(args[1], env),
229
+ env=env,
230
+ )
231
+
232
+ # Number
233
+ elif first.value == "+":
234
+ evaluated_args = [self.evaluate_expression(arg, env) for arg in expr.elements[1:]]
235
+
236
+ # Check for non-integer arguments
237
+ if not all(isinstance(arg, Expr.Integer) for arg in evaluated_args):
238
+ return Expr.Error(
239
+ category="TypeError",
240
+ message="All arguments to + must be integers"
241
+ )
242
+
243
+ # Sum up the integer values
244
+ result = sum(arg.value for arg in evaluated_args)
245
+ return Expr.Integer(result)
246
+
247
+ else:
248
+ evaluated_elements = [self.evaluate_expression(e, env) for e in expr.elements]
249
+ return Expr.ListExpr(evaluated_elements)
250
+
251
+ elif isinstance(expr, Expr.Function):
252
+ return expr
253
+
254
+ else:
255
+ raise ValueError(f"Unknown expression type: {type(expr)}")
@@ -1,5 +1,5 @@
1
1
  from typing import Dict, Optional
2
- from src.astreum.machine.expression import Expr
2
+ from astreum.lispeum.expression import Expr
3
3
 
4
4
 
5
5
  class Environment:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: astreum
3
- Version: 0.1.3
3
+ Version: 0.1.5
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
@@ -20,5 +20,5 @@ Python library to interact with the Astreum blockchain and its Lispeum virtual m
20
20
 
21
21
  ## Testing
22
22
 
23
- python -m unittest discover -s tests
23
+ python3 -m unittest discover -s tests
24
24
 
@@ -0,0 +1,28 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/astreum/__init__.py
5
+ src/astreum.egg-info/PKG-INFO
6
+ src/astreum.egg-info/SOURCES.txt
7
+ src/astreum.egg-info/dependency_links.txt
8
+ src/astreum.egg-info/top_level.txt
9
+ src/astreum/lispeum/__init__.py
10
+ src/astreum/lispeum/expression.py
11
+ src/astreum/lispeum/parser.py
12
+ src/astreum/lispeum/tokenizer.py
13
+ src/astreum/lispeum/special/__init__.py
14
+ src/astreum/lispeum/special/definition.py
15
+ src/astreum/lispeum/special/list/__init__.py
16
+ src/astreum/lispeum/special/list/all.py
17
+ src/astreum/lispeum/special/list/any.py
18
+ src/astreum/lispeum/special/list/fold.py
19
+ src/astreum/lispeum/special/list/get.py
20
+ src/astreum/lispeum/special/list/insert.py
21
+ src/astreum/lispeum/special/list/map.py
22
+ src/astreum/lispeum/special/list/position.py
23
+ src/astreum/lispeum/special/list/remove.py
24
+ src/astreum/lispeum/special/number/__init__.py
25
+ src/astreum/lispeum/special/number/addition.py
26
+ src/astreum/machine/__init__.py
27
+ src/astreum/machine/environment.py
28
+ src/astreum/machine/error.py
@@ -1,121 +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
- 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,50 +0,0 @@
1
- from typing import List
2
-
3
- class Expr:
4
- class ListExpr:
5
- def __init__(self, elements: List['Expr']):
6
- self.elements = elements
7
-
8
- @property
9
- def value(self):
10
- inner = " ".join(str(e) for e in self.elements)
11
- return f"({inner})"
12
-
13
-
14
- def __repr__(self):
15
- if not self.elements:
16
- return "()"
17
-
18
- inner = " ".join(str(e) for e in self.elements)
19
- return f"({inner})"
20
-
21
- class Symbol:
22
- def __init__(self, value: str):
23
- self.value = value
24
-
25
- def __repr__(self):
26
- return self.value
27
-
28
- class Integer:
29
- def __init__(self, value: int):
30
- self.value = value
31
-
32
- def __repr__(self):
33
- return str(self.value)
34
-
35
- class String:
36
- def __init__(self, value: str):
37
- self.value = value
38
-
39
- def __repr__(self):
40
- return f'"{self.value}"'
41
-
42
- class Function:
43
- def __init__(self, params: List[str], body: 'Expr'):
44
- self.params = params
45
- self.body = body
46
-
47
- def __repr__(self):
48
- params_str = " ".join(self.params)
49
- body_str = str(self.body)
50
- return f"(fn ({params_str}) {body_str})"
@@ -1,14 +0,0 @@
1
- LICENSE
2
- README.md
3
- pyproject.toml
4
- src/astreum/__init__.py
5
- src/astreum.egg-info/PKG-INFO
6
- src/astreum.egg-info/SOURCES.txt
7
- src/astreum.egg-info/dependency_links.txt
8
- src/astreum.egg-info/top_level.txt
9
- src/astreum/machine/__init__.py
10
- src/astreum/machine/environment.py
11
- src/astreum/machine/error.py
12
- src/astreum/machine/expression.py
13
- src/astreum/machine/parser.py
14
- src/astreum/machine/tokenizer.py
File without changes
File without changes
File without changes