blackspace 0.0.47__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.
- blackspace-0.0.47/PKG-INFO +34 -0
- blackspace-0.0.47/README.md +8 -0
- blackspace-0.0.47/blackspace.egg-info/PKG-INFO +34 -0
- blackspace-0.0.47/blackspace.egg-info/SOURCES.txt +73 -0
- blackspace-0.0.47/blackspace.egg-info/dependency_links.txt +1 -0
- blackspace-0.0.47/blackspace.egg-info/entry_points.txt +2 -0
- blackspace-0.0.47/blackspace.egg-info/requires.txt +24 -0
- blackspace-0.0.47/blackspace.egg-info/top_level.txt +1 -0
- blackspace-0.0.47/blackspace_compiler/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/__main__.py +6 -0
- blackspace-0.0.47/blackspace_compiler/ast/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/__init__.py +4 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/evaluation_context.py +75 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/function_registry.py +45 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/heap/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/heap/consts.py +16 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/heap/helpers.py +75 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/issue_level.py +6 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/label_registry.py +8 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/stack/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/context/stack/helpers.py +76 -0
- blackspace-0.0.47/blackspace_compiler/ast/debugstatement.py +27 -0
- blackspace-0.0.47/blackspace_compiler/ast/definitions/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/definitions/function.py +60 -0
- blackspace-0.0.47/blackspace_compiler/ast/definitions/variable.py +10 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/builtinfunctions/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/builtinfunctions/sizeof.py +21 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/expression.py +16 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/functions/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/functions/function_call.py +124 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/array.py +58 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/bool.py +28 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/int.py +20 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/string.py +21 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/arithmetic.py +64 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/boolean.py +85 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/comparison.py +158 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/operator.py +65 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/array_index.py +64 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/memory_fetch.py +37 -0
- blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/variable_access.py +40 -0
- blackspace-0.0.47/blackspace_compiler/ast/node.py +5 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/assignment.py +44 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/compound.py +24 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/condition.py +55 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/function.py +99 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/io.py +99 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/loop.py +116 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/program.py +68 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/statement.py +10 -0
- blackspace-0.0.47/blackspace_compiler/ast/statements/void.py +15 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/complex/array.py +16 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/complex/memory.py +16 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/complex/string.py +12 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/primitives/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/primitives/bool.py +12 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/primitives/int.py +12 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/primitives/void.py +12 -0
- blackspace-0.0.47/blackspace_compiler/ast/types/type.py +9 -0
- blackspace-0.0.47/blackspace_compiler/ast/utils/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/ast/utils/error_message.py +7 -0
- blackspace-0.0.47/blackspace_compiler/ast/utils/indent.py +5 -0
- blackspace-0.0.47/blackspace_compiler/whitespace/__init__.py +0 -0
- blackspace-0.0.47/blackspace_compiler/whitespace/converters.py +21 -0
- blackspace-0.0.47/blackspace_compiler/whitespace/printstr.py +9 -0
- blackspace-0.0.47/blackspace_compiler/whitespace/snippets.py +31 -0
- blackspace-0.0.47/blackspace_compiler/whitespace/tokens.py +3 -0
- blackspace-0.0.47/pyproject.toml +50 -0
- blackspace-0.0.47/setup.cfg +4 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blackspace
|
|
3
|
+
Version: 0.0.47
|
|
4
|
+
Summary: BlackSpace
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Provides-Extra: build
|
|
8
|
+
Requires-Dist: pip-tools; extra == "build"
|
|
9
|
+
Requires-Dist: setuptools; extra == "build"
|
|
10
|
+
Requires-Dist: twine; extra == "build"
|
|
11
|
+
Provides-Extra: publish
|
|
12
|
+
Requires-Dist: twine; extra == "publish"
|
|
13
|
+
Provides-Extra: types
|
|
14
|
+
Requires-Dist: types-mock; extra == "types"
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: black; extra == "test"
|
|
17
|
+
Requires-Dist: mock; extra == "test"
|
|
18
|
+
Requires-Dist: mypy; extra == "test"
|
|
19
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
20
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
21
|
+
Requires-Dist: pytest-mypy; extra == "test"
|
|
22
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Provides-Extra: dotenv
|
|
25
|
+
Requires-Dist: python-dotenv; extra == "dotenv"
|
|
26
|
+
|
|
27
|
+
# BlackSpace
|
|
28
|
+
|
|
29
|
+
BlackSpace is a programming language that compiles to WhiteSpace, an esoteric programming language
|
|
30
|
+
that uses only spaces, tabs, and linefeeds as syntax. BlackSpace is designed to make it easier
|
|
31
|
+
to write and read WhiteSpace programs by providing a higher-level syntax.
|
|
32
|
+
|
|
33
|
+
Currently the parser is not implemented, and only the compiler from an AST to WhiteSpace
|
|
34
|
+
is available.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# BlackSpace
|
|
2
|
+
|
|
3
|
+
BlackSpace is a programming language that compiles to WhiteSpace, an esoteric programming language
|
|
4
|
+
that uses only spaces, tabs, and linefeeds as syntax. BlackSpace is designed to make it easier
|
|
5
|
+
to write and read WhiteSpace programs by providing a higher-level syntax.
|
|
6
|
+
|
|
7
|
+
Currently the parser is not implemented, and only the compiler from an AST to WhiteSpace
|
|
8
|
+
is available.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blackspace
|
|
3
|
+
Version: 0.0.47
|
|
4
|
+
Summary: BlackSpace
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Provides-Extra: build
|
|
8
|
+
Requires-Dist: pip-tools; extra == "build"
|
|
9
|
+
Requires-Dist: setuptools; extra == "build"
|
|
10
|
+
Requires-Dist: twine; extra == "build"
|
|
11
|
+
Provides-Extra: publish
|
|
12
|
+
Requires-Dist: twine; extra == "publish"
|
|
13
|
+
Provides-Extra: types
|
|
14
|
+
Requires-Dist: types-mock; extra == "types"
|
|
15
|
+
Provides-Extra: test
|
|
16
|
+
Requires-Dist: black; extra == "test"
|
|
17
|
+
Requires-Dist: mock; extra == "test"
|
|
18
|
+
Requires-Dist: mypy; extra == "test"
|
|
19
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
20
|
+
Requires-Dist: pytest-mock; extra == "test"
|
|
21
|
+
Requires-Dist: pytest-mypy; extra == "test"
|
|
22
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Provides-Extra: dotenv
|
|
25
|
+
Requires-Dist: python-dotenv; extra == "dotenv"
|
|
26
|
+
|
|
27
|
+
# BlackSpace
|
|
28
|
+
|
|
29
|
+
BlackSpace is a programming language that compiles to WhiteSpace, an esoteric programming language
|
|
30
|
+
that uses only spaces, tabs, and linefeeds as syntax. BlackSpace is designed to make it easier
|
|
31
|
+
to write and read WhiteSpace programs by providing a higher-level syntax.
|
|
32
|
+
|
|
33
|
+
Currently the parser is not implemented, and only the compiler from an AST to WhiteSpace
|
|
34
|
+
is available.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
blackspace.egg-info/PKG-INFO
|
|
4
|
+
blackspace.egg-info/SOURCES.txt
|
|
5
|
+
blackspace.egg-info/dependency_links.txt
|
|
6
|
+
blackspace.egg-info/entry_points.txt
|
|
7
|
+
blackspace.egg-info/requires.txt
|
|
8
|
+
blackspace.egg-info/top_level.txt
|
|
9
|
+
blackspace_compiler/__init__.py
|
|
10
|
+
blackspace_compiler/__main__.py
|
|
11
|
+
blackspace_compiler/ast/__init__.py
|
|
12
|
+
blackspace_compiler/ast/debugstatement.py
|
|
13
|
+
blackspace_compiler/ast/node.py
|
|
14
|
+
blackspace_compiler/ast/context/__init__.py
|
|
15
|
+
blackspace_compiler/ast/context/evaluation_context.py
|
|
16
|
+
blackspace_compiler/ast/context/function_registry.py
|
|
17
|
+
blackspace_compiler/ast/context/issue_level.py
|
|
18
|
+
blackspace_compiler/ast/context/label_registry.py
|
|
19
|
+
blackspace_compiler/ast/context/heap/__init__.py
|
|
20
|
+
blackspace_compiler/ast/context/heap/consts.py
|
|
21
|
+
blackspace_compiler/ast/context/heap/helpers.py
|
|
22
|
+
blackspace_compiler/ast/context/stack/__init__.py
|
|
23
|
+
blackspace_compiler/ast/context/stack/helpers.py
|
|
24
|
+
blackspace_compiler/ast/definitions/__init__.py
|
|
25
|
+
blackspace_compiler/ast/definitions/function.py
|
|
26
|
+
blackspace_compiler/ast/definitions/variable.py
|
|
27
|
+
blackspace_compiler/ast/expressions/__init__.py
|
|
28
|
+
blackspace_compiler/ast/expressions/expression.py
|
|
29
|
+
blackspace_compiler/ast/expressions/builtinfunctions/__init__.py
|
|
30
|
+
blackspace_compiler/ast/expressions/builtinfunctions/sizeof.py
|
|
31
|
+
blackspace_compiler/ast/expressions/functions/__init__.py
|
|
32
|
+
blackspace_compiler/ast/expressions/functions/function_call.py
|
|
33
|
+
blackspace_compiler/ast/expressions/literals/__init__.py
|
|
34
|
+
blackspace_compiler/ast/expressions/literals/array.py
|
|
35
|
+
blackspace_compiler/ast/expressions/literals/bool.py
|
|
36
|
+
blackspace_compiler/ast/expressions/literals/int.py
|
|
37
|
+
blackspace_compiler/ast/expressions/literals/string.py
|
|
38
|
+
blackspace_compiler/ast/expressions/operations/__init__.py
|
|
39
|
+
blackspace_compiler/ast/expressions/operations/arithmetic.py
|
|
40
|
+
blackspace_compiler/ast/expressions/operations/boolean.py
|
|
41
|
+
blackspace_compiler/ast/expressions/operations/comparison.py
|
|
42
|
+
blackspace_compiler/ast/expressions/operations/operator.py
|
|
43
|
+
blackspace_compiler/ast/expressions/variables/__init__.py
|
|
44
|
+
blackspace_compiler/ast/expressions/variables/array_index.py
|
|
45
|
+
blackspace_compiler/ast/expressions/variables/memory_fetch.py
|
|
46
|
+
blackspace_compiler/ast/expressions/variables/variable_access.py
|
|
47
|
+
blackspace_compiler/ast/statements/__init__.py
|
|
48
|
+
blackspace_compiler/ast/statements/assignment.py
|
|
49
|
+
blackspace_compiler/ast/statements/compound.py
|
|
50
|
+
blackspace_compiler/ast/statements/condition.py
|
|
51
|
+
blackspace_compiler/ast/statements/function.py
|
|
52
|
+
blackspace_compiler/ast/statements/io.py
|
|
53
|
+
blackspace_compiler/ast/statements/loop.py
|
|
54
|
+
blackspace_compiler/ast/statements/program.py
|
|
55
|
+
blackspace_compiler/ast/statements/statement.py
|
|
56
|
+
blackspace_compiler/ast/statements/void.py
|
|
57
|
+
blackspace_compiler/ast/types/__init__.py
|
|
58
|
+
blackspace_compiler/ast/types/type.py
|
|
59
|
+
blackspace_compiler/ast/types/complex/array.py
|
|
60
|
+
blackspace_compiler/ast/types/complex/memory.py
|
|
61
|
+
blackspace_compiler/ast/types/complex/string.py
|
|
62
|
+
blackspace_compiler/ast/types/primitives/__init__.py
|
|
63
|
+
blackspace_compiler/ast/types/primitives/bool.py
|
|
64
|
+
blackspace_compiler/ast/types/primitives/int.py
|
|
65
|
+
blackspace_compiler/ast/types/primitives/void.py
|
|
66
|
+
blackspace_compiler/ast/utils/__init__.py
|
|
67
|
+
blackspace_compiler/ast/utils/error_message.py
|
|
68
|
+
blackspace_compiler/ast/utils/indent.py
|
|
69
|
+
blackspace_compiler/whitespace/__init__.py
|
|
70
|
+
blackspace_compiler/whitespace/converters.py
|
|
71
|
+
blackspace_compiler/whitespace/printstr.py
|
|
72
|
+
blackspace_compiler/whitespace/snippets.py
|
|
73
|
+
blackspace_compiler/whitespace/tokens.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
[build]
|
|
3
|
+
pip-tools
|
|
4
|
+
setuptools
|
|
5
|
+
twine
|
|
6
|
+
|
|
7
|
+
[dotenv]
|
|
8
|
+
python-dotenv
|
|
9
|
+
|
|
10
|
+
[publish]
|
|
11
|
+
twine
|
|
12
|
+
|
|
13
|
+
[test]
|
|
14
|
+
black
|
|
15
|
+
mock
|
|
16
|
+
mypy
|
|
17
|
+
pytest-cov
|
|
18
|
+
pytest-mock
|
|
19
|
+
pytest-mypy
|
|
20
|
+
pytest-timeout
|
|
21
|
+
pytest
|
|
22
|
+
|
|
23
|
+
[types]
|
|
24
|
+
types-mock
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blackspace_compiler
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from ..node import AstNode
|
|
8
|
+
from .function_registry import FunctionRegistry
|
|
9
|
+
from .issue_level import IssueLevel
|
|
10
|
+
from .label_registry import LabelRegistry
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Issue:
|
|
15
|
+
level: IssueLevel
|
|
16
|
+
node: AstNode
|
|
17
|
+
message: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@contextmanager
|
|
21
|
+
def increment_stack(context: EvaluationContext, by: int = 1):
|
|
22
|
+
try:
|
|
23
|
+
context._stack_offset += by
|
|
24
|
+
yield
|
|
25
|
+
finally:
|
|
26
|
+
context._stack_offset -= by
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@contextmanager
|
|
30
|
+
def reset_stack(context: EvaluationContext):
|
|
31
|
+
try:
|
|
32
|
+
original_offset = context._stack_offset
|
|
33
|
+
context._stack_offset = 0
|
|
34
|
+
yield
|
|
35
|
+
finally:
|
|
36
|
+
if context._stack_offset != 0:
|
|
37
|
+
raise RuntimeError("Stack offset must be zero when exiting reset_stack context")
|
|
38
|
+
context._stack_offset = original_offset
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class EvaluationContext:
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
self.label_registry = LabelRegistry()
|
|
44
|
+
self.function_registry = FunctionRegistry()
|
|
45
|
+
|
|
46
|
+
self._issues: list[Issue] = []
|
|
47
|
+
self._stack_offset = 0
|
|
48
|
+
|
|
49
|
+
def register_issue(self, level: IssueLevel, node: AstNode, message: str) -> None:
|
|
50
|
+
self._issues.append(Issue(level, node, message))
|
|
51
|
+
|
|
52
|
+
def with_incremented_stack(self, fn: Callable[[EvaluationContext], str]) -> str:
|
|
53
|
+
with increment_stack(self):
|
|
54
|
+
return fn(self)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def stack_offset(self) -> int:
|
|
58
|
+
return self._stack_offset
|
|
59
|
+
|
|
60
|
+
def print_issues(self, strict: bool = False) -> None:
|
|
61
|
+
has_warnings = False
|
|
62
|
+
has_errors = False
|
|
63
|
+
|
|
64
|
+
for issue in self._issues:
|
|
65
|
+
print(f"[{issue.level.name}] {issue.message} (at {issue.node})")
|
|
66
|
+
|
|
67
|
+
if issue.level == IssueLevel.ERROR:
|
|
68
|
+
has_errors = True
|
|
69
|
+
elif issue.level == IssueLevel.WARNING:
|
|
70
|
+
has_warnings = True
|
|
71
|
+
|
|
72
|
+
if has_errors:
|
|
73
|
+
raise RuntimeError("Compilation failed due to errors.")
|
|
74
|
+
if strict and has_warnings:
|
|
75
|
+
raise RuntimeError("Compilation failed due to warnings in strict mode.")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
|
|
3
|
+
from ..definitions.function import FunctionSignature
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FunctionRegistry:
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
self._functions: list[FunctionSignature] = []
|
|
9
|
+
self._labels: dict[str, int] = {}
|
|
10
|
+
self._current_function: FunctionSignature | None = None
|
|
11
|
+
|
|
12
|
+
def register_function(self, function: FunctionSignature, label_id: int) -> None:
|
|
13
|
+
if any(fn.name == function.name for fn in self._functions):
|
|
14
|
+
raise RuntimeError(f"Function '{function.name}' is already registered.")
|
|
15
|
+
|
|
16
|
+
self._functions.append(function)
|
|
17
|
+
self._labels[function.name] = label_id
|
|
18
|
+
|
|
19
|
+
def get_function_definition(self, function_name: str) -> FunctionSignature | None:
|
|
20
|
+
for function in self._functions:
|
|
21
|
+
if function.name == function_name:
|
|
22
|
+
return function
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
def get_function_label(self, function_name: str) -> int:
|
|
26
|
+
"""
|
|
27
|
+
Get the label ID for the given function name.
|
|
28
|
+
Should not be called unless the function is known to exist by using
|
|
29
|
+
`get_function_definition` first.
|
|
30
|
+
"""
|
|
31
|
+
return self._labels[function_name]
|
|
32
|
+
|
|
33
|
+
@contextmanager
|
|
34
|
+
def function_context(self, function: FunctionSignature):
|
|
35
|
+
if self._current_function is not None:
|
|
36
|
+
raise RuntimeError("Nested function definitions are not supported.")
|
|
37
|
+
self._current_function = function
|
|
38
|
+
try:
|
|
39
|
+
yield
|
|
40
|
+
finally:
|
|
41
|
+
self._current_function = None
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def current_function(self) -> FunctionSignature | None:
|
|
45
|
+
return self._current_function
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
REGISTER_A = 0
|
|
2
|
+
REGISTER_B = 1
|
|
3
|
+
REGISTER_C = 2
|
|
4
|
+
REGISTER_D = 3
|
|
5
|
+
|
|
6
|
+
_max_parameter_count = 15
|
|
7
|
+
_function_parameter_start = REGISTER_D + 1
|
|
8
|
+
FUNCTION_PARAMETER_REGISTERS = list(
|
|
9
|
+
range(_function_parameter_start, _function_parameter_start + _max_parameter_count)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
FIRST_PROGRAM_STACK_HEAP_ADDRESS = max(FUNCTION_PARAMETER_REGISTERS) + 1
|
|
13
|
+
PROGRAM_STACK_LIMIT = FIRST_PROGRAM_STACK_HEAP_ADDRESS + 2048 * 128
|
|
14
|
+
|
|
15
|
+
FREE_HEAP_ALLOCATION_COUNTER = PROGRAM_STACK_LIMIT + 1
|
|
16
|
+
FREE_HEAP_START = FREE_HEAP_ALLOCATION_COUNTER + 1
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ....whitespace.snippets import ADD, DUPLICATE, FETCH, PUSH, STORE, SWAP
|
|
6
|
+
from ...debugstatement import debug_statement
|
|
7
|
+
from .consts import FREE_HEAP_ALLOCATION_COUNTER, FREE_HEAP_START
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..evaluation_context import EvaluationContext
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def initialize_heap(context: EvaluationContext) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Initialize the heap by setting up the heap allocation counter.
|
|
16
|
+
This function should be called once at the start of the program execution.
|
|
17
|
+
"""
|
|
18
|
+
_ = context # Unused
|
|
19
|
+
return (
|
|
20
|
+
debug_statement("Initializing heap")
|
|
21
|
+
+ PUSH(FREE_HEAP_ALLOCATION_COUNTER)
|
|
22
|
+
+ PUSH(FREE_HEAP_START)
|
|
23
|
+
+ STORE
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def place_const_data_on_heap(context: EvaluationContext, data: list[int]) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Place constant data on the heap and return the starting address.
|
|
30
|
+
This can be used for string literals or other constant data.
|
|
31
|
+
The context stack offset is NOT incremented, that is the responsibility of the caller.
|
|
32
|
+
"""
|
|
33
|
+
_ = context # Unused
|
|
34
|
+
|
|
35
|
+
# get the current free heap location address
|
|
36
|
+
res = (
|
|
37
|
+
debug_statement(f"Placing {len(data)} long const data on heap")
|
|
38
|
+
+ PUSH(FREE_HEAP_ALLOCATION_COUNTER)
|
|
39
|
+
+ FETCH
|
|
40
|
+
+ DUPLICATE
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
data = [len(data)] + data # prepend length
|
|
44
|
+
|
|
45
|
+
for i in range(len(data)):
|
|
46
|
+
res += DUPLICATE
|
|
47
|
+
res += PUSH(data[i]) + STORE
|
|
48
|
+
res += PUSH(1) + ADD
|
|
49
|
+
res += PUSH(FREE_HEAP_ALLOCATION_COUNTER) + SWAP + STORE
|
|
50
|
+
return res
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def allocate_data_on_heap(context: EvaluationContext, size: int) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Allocate space on the heap for a given size and return the starting address.
|
|
56
|
+
The first word at that address will contain the size of the allocated block.
|
|
57
|
+
The context stack offset is NOT incremented, that is the responsibility of the caller.
|
|
58
|
+
"""
|
|
59
|
+
_ = context # Unused
|
|
60
|
+
|
|
61
|
+
# get the current free heap location address
|
|
62
|
+
res = (
|
|
63
|
+
debug_statement(f"Allocating {size} long space on heap")
|
|
64
|
+
+ PUSH(FREE_HEAP_ALLOCATION_COUNTER)
|
|
65
|
+
+ FETCH
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# on the free location, first store the size
|
|
69
|
+
res += DUPLICATE + PUSH(size) + STORE
|
|
70
|
+
|
|
71
|
+
# increment the free heap location by size + 1
|
|
72
|
+
res += DUPLICATE + PUSH(size + 1) + ADD
|
|
73
|
+
res += PUSH(FREE_HEAP_ALLOCATION_COUNTER) + SWAP + STORE
|
|
74
|
+
|
|
75
|
+
return res
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ....whitespace.snippets import ADD, COPY, DUPLICATE, FETCH, POP, PUSH, STORE
|
|
6
|
+
from ...debugstatement import debug_statement
|
|
7
|
+
from ..heap.consts import FIRST_PROGRAM_STACK_HEAP_ADDRESS
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..evaluation_context import EvaluationContext
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# A function stack is a stack frame used during the evaluation of functions.
|
|
14
|
+
# When entering a function, a new stack frame is created to hold local variables and state.
|
|
15
|
+
# When exiting a function, the stack frame is removed, and control returns to the previous stack.
|
|
16
|
+
# The data of the stack frames is stored on the runtime heap.
|
|
17
|
+
# The stack data store location is pushed to the whitespace stack.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_datastack_location(context: EvaluationContext) -> str:
|
|
21
|
+
"""Get the heap location of the current data stack."""
|
|
22
|
+
return COPY(context.stack_offset)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def enter_first_stack(context: EvaluationContext) -> str:
|
|
26
|
+
"""Enter the first stack of the execution context."""
|
|
27
|
+
_ = context # Unused
|
|
28
|
+
return (
|
|
29
|
+
debug_statement("Entering first stack")
|
|
30
|
+
+ PUSH(FIRST_PROGRAM_STACK_HEAP_ADDRESS)
|
|
31
|
+
+ DUPLICATE
|
|
32
|
+
+ PUSH(1)
|
|
33
|
+
+ STORE
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def enter_new_stack(context: EvaluationContext) -> str:
|
|
38
|
+
"""Enter a new stack in the execution context. Assumes there is already at least one stack."""
|
|
39
|
+
# Get the location of the current stack, get the length of the current stack,
|
|
40
|
+
# add them together to the the start of the new stack.
|
|
41
|
+
# Push 1 to the new stack to indicate its length is starting at 1.
|
|
42
|
+
res = debug_statement(f"Entering new stack with stack offset {context.stack_offset}")
|
|
43
|
+
res += _get_datastack_location(context)
|
|
44
|
+
res += DUPLICATE + FETCH + ADD
|
|
45
|
+
res += DUPLICATE + PUSH(1) + STORE
|
|
46
|
+
return res
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def exit_stack(context: EvaluationContext) -> str:
|
|
50
|
+
"""Exit the current stack in the execution context."""
|
|
51
|
+
_ = context # Unused
|
|
52
|
+
return debug_statement(f"Exiting stack with stack offset {context.stack_offset}") + POP
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def silently_allocate_on_datastack(context: EvaluationContext, length: int) -> str:
|
|
56
|
+
"""
|
|
57
|
+
Allocate data on the current data stack.
|
|
58
|
+
The starting location of the data is NOT pushed onto the whitespace stack.
|
|
59
|
+
"""
|
|
60
|
+
res = _get_datastack_location(context)
|
|
61
|
+
res += DUPLICATE + FETCH + PUSH(length) + ADD + STORE
|
|
62
|
+
return res
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_datastack_location(context: EvaluationContext, offset: int) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Get the heap location of the current data stack plus the given offset.
|
|
68
|
+
**Skips the first element of the data stack, which is the length of the stack.**
|
|
69
|
+
The resulting location is pushed onto the whitespace stack.
|
|
70
|
+
The context stack offset is NOT incremented, that is the responsibility of the caller.
|
|
71
|
+
"""
|
|
72
|
+
offset += 1 # Skip the length element
|
|
73
|
+
res = _get_datastack_location(context)
|
|
74
|
+
if offset != 0:
|
|
75
|
+
res += PUSH(offset) + ADD
|
|
76
|
+
return res
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from ..whitespace.printstr import print_str
|
|
2
|
+
|
|
3
|
+
ENABLE_DEBUG = False
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def debug_statement(text: str) -> str:
|
|
7
|
+
if not ENABLE_DEBUG:
|
|
8
|
+
return ""
|
|
9
|
+
return (
|
|
10
|
+
code_comment(f"DEBUG: {text}")
|
|
11
|
+
+ print_str(f"DEBUG: {text}\n")
|
|
12
|
+
+ code_comment("// end of debug statement print")
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def debug_instructions(instructions: str) -> str:
|
|
17
|
+
if not ENABLE_DEBUG:
|
|
18
|
+
return ""
|
|
19
|
+
return instructions
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def code_comment(text: str) -> str:
|
|
23
|
+
if "\n" in text:
|
|
24
|
+
raise ValueError("Code comments cannot contain newlines")
|
|
25
|
+
if "\t" in text:
|
|
26
|
+
raise ValueError("Code comments cannot contain tabs")
|
|
27
|
+
return text.replace(" ", "·")
|
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from ..types.type import Type
|
|
6
|
+
from .variable import VariableDefinition
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ParameterDefinition:
|
|
11
|
+
name: str
|
|
12
|
+
type: Type
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class FunctionSignature:
|
|
17
|
+
name: str
|
|
18
|
+
parameters: list[ParameterDefinition]
|
|
19
|
+
return_type: Type
|
|
20
|
+
variables: list[VariableDefinition]
|
|
21
|
+
|
|
22
|
+
def assert_correct_variable_configuration(self) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Validates that the function variable setup is correct according to the parameters.
|
|
25
|
+
Each parameter will be stored into a variable by the same name. The first N variables
|
|
26
|
+
must match the N parameters.
|
|
27
|
+
"""
|
|
28
|
+
if len(self.variables) < len(self.parameters):
|
|
29
|
+
raise RuntimeError(
|
|
30
|
+
f"Function '{self.name}' has fewer variables ({len(self.variables)}) "
|
|
31
|
+
f"than parameters ({len(self.parameters)})."
|
|
32
|
+
)
|
|
33
|
+
for i in range(len(self.parameters)):
|
|
34
|
+
param = self.parameters[i]
|
|
35
|
+
var = self.variables[i]
|
|
36
|
+
if param.name != var.name:
|
|
37
|
+
raise RuntimeError(
|
|
38
|
+
f"Function '{self.name}' parameter {i} name '{param.name}' does not match "
|
|
39
|
+
f"variable name '{var.name}'."
|
|
40
|
+
)
|
|
41
|
+
if param.type != var.type:
|
|
42
|
+
raise RuntimeError(
|
|
43
|
+
f"Function '{self.name}' parameter {i} type '{param.type}' does not match "
|
|
44
|
+
f"variable type '{var.type}'."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def get_variable(self, name: str) -> VariableOffset | None:
|
|
48
|
+
offset = 0
|
|
49
|
+
for variable in self.variables:
|
|
50
|
+
if variable.name == name:
|
|
51
|
+
return VariableOffset(definition=variable, offset=offset)
|
|
52
|
+
else:
|
|
53
|
+
offset += variable.type.get_size()
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class VariableOffset:
|
|
59
|
+
definition: VariableDefinition
|
|
60
|
+
offset: int
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from ...context import EvaluationContext
|
|
2
|
+
from ...types.primitives.int import IntegerType
|
|
3
|
+
from ..expression import Expression
|
|
4
|
+
from ..literals.int import IntLiteral
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SizeofExpression(Expression):
|
|
8
|
+
def __init__(self, inner_expression: Expression):
|
|
9
|
+
super().__init__()
|
|
10
|
+
self.inner_expression = inner_expression
|
|
11
|
+
|
|
12
|
+
def __repr__(self):
|
|
13
|
+
return f"sizeof({self.inner_expression})"
|
|
14
|
+
|
|
15
|
+
def get_type(self, context):
|
|
16
|
+
return IntegerType()
|
|
17
|
+
|
|
18
|
+
def evaluate(self, context: EvaluationContext) -> str:
|
|
19
|
+
inner_type = self.inner_expression.get_type(context)
|
|
20
|
+
size = inner_type.get_size()
|
|
21
|
+
return IntLiteral(size).evaluate(context)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from ..context import EvaluationContext
|
|
4
|
+
from ..node import AstNode
|
|
5
|
+
from ..types.type import Type
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Expression(AstNode, ABC):
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def get_type(self, context: EvaluationContext) -> Type:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def evaluate(self, context: EvaluationContext) -> str:
|
|
16
|
+
pass
|
|
File without changes
|