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.
Files changed (75) hide show
  1. blackspace-0.0.47/PKG-INFO +34 -0
  2. blackspace-0.0.47/README.md +8 -0
  3. blackspace-0.0.47/blackspace.egg-info/PKG-INFO +34 -0
  4. blackspace-0.0.47/blackspace.egg-info/SOURCES.txt +73 -0
  5. blackspace-0.0.47/blackspace.egg-info/dependency_links.txt +1 -0
  6. blackspace-0.0.47/blackspace.egg-info/entry_points.txt +2 -0
  7. blackspace-0.0.47/blackspace.egg-info/requires.txt +24 -0
  8. blackspace-0.0.47/blackspace.egg-info/top_level.txt +1 -0
  9. blackspace-0.0.47/blackspace_compiler/__init__.py +0 -0
  10. blackspace-0.0.47/blackspace_compiler/__main__.py +6 -0
  11. blackspace-0.0.47/blackspace_compiler/ast/__init__.py +0 -0
  12. blackspace-0.0.47/blackspace_compiler/ast/context/__init__.py +4 -0
  13. blackspace-0.0.47/blackspace_compiler/ast/context/evaluation_context.py +75 -0
  14. blackspace-0.0.47/blackspace_compiler/ast/context/function_registry.py +45 -0
  15. blackspace-0.0.47/blackspace_compiler/ast/context/heap/__init__.py +0 -0
  16. blackspace-0.0.47/blackspace_compiler/ast/context/heap/consts.py +16 -0
  17. blackspace-0.0.47/blackspace_compiler/ast/context/heap/helpers.py +75 -0
  18. blackspace-0.0.47/blackspace_compiler/ast/context/issue_level.py +6 -0
  19. blackspace-0.0.47/blackspace_compiler/ast/context/label_registry.py +8 -0
  20. blackspace-0.0.47/blackspace_compiler/ast/context/stack/__init__.py +0 -0
  21. blackspace-0.0.47/blackspace_compiler/ast/context/stack/helpers.py +76 -0
  22. blackspace-0.0.47/blackspace_compiler/ast/debugstatement.py +27 -0
  23. blackspace-0.0.47/blackspace_compiler/ast/definitions/__init__.py +0 -0
  24. blackspace-0.0.47/blackspace_compiler/ast/definitions/function.py +60 -0
  25. blackspace-0.0.47/blackspace_compiler/ast/definitions/variable.py +10 -0
  26. blackspace-0.0.47/blackspace_compiler/ast/expressions/__init__.py +0 -0
  27. blackspace-0.0.47/blackspace_compiler/ast/expressions/builtinfunctions/__init__.py +0 -0
  28. blackspace-0.0.47/blackspace_compiler/ast/expressions/builtinfunctions/sizeof.py +21 -0
  29. blackspace-0.0.47/blackspace_compiler/ast/expressions/expression.py +16 -0
  30. blackspace-0.0.47/blackspace_compiler/ast/expressions/functions/__init__.py +0 -0
  31. blackspace-0.0.47/blackspace_compiler/ast/expressions/functions/function_call.py +124 -0
  32. blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/__init__.py +0 -0
  33. blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/array.py +58 -0
  34. blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/bool.py +28 -0
  35. blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/int.py +20 -0
  36. blackspace-0.0.47/blackspace_compiler/ast/expressions/literals/string.py +21 -0
  37. blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/__init__.py +0 -0
  38. blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/arithmetic.py +64 -0
  39. blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/boolean.py +85 -0
  40. blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/comparison.py +158 -0
  41. blackspace-0.0.47/blackspace_compiler/ast/expressions/operations/operator.py +65 -0
  42. blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/__init__.py +0 -0
  43. blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/array_index.py +64 -0
  44. blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/memory_fetch.py +37 -0
  45. blackspace-0.0.47/blackspace_compiler/ast/expressions/variables/variable_access.py +40 -0
  46. blackspace-0.0.47/blackspace_compiler/ast/node.py +5 -0
  47. blackspace-0.0.47/blackspace_compiler/ast/statements/__init__.py +0 -0
  48. blackspace-0.0.47/blackspace_compiler/ast/statements/assignment.py +44 -0
  49. blackspace-0.0.47/blackspace_compiler/ast/statements/compound.py +24 -0
  50. blackspace-0.0.47/blackspace_compiler/ast/statements/condition.py +55 -0
  51. blackspace-0.0.47/blackspace_compiler/ast/statements/function.py +99 -0
  52. blackspace-0.0.47/blackspace_compiler/ast/statements/io.py +99 -0
  53. blackspace-0.0.47/blackspace_compiler/ast/statements/loop.py +116 -0
  54. blackspace-0.0.47/blackspace_compiler/ast/statements/program.py +68 -0
  55. blackspace-0.0.47/blackspace_compiler/ast/statements/statement.py +10 -0
  56. blackspace-0.0.47/blackspace_compiler/ast/statements/void.py +15 -0
  57. blackspace-0.0.47/blackspace_compiler/ast/types/__init__.py +0 -0
  58. blackspace-0.0.47/blackspace_compiler/ast/types/complex/array.py +16 -0
  59. blackspace-0.0.47/blackspace_compiler/ast/types/complex/memory.py +16 -0
  60. blackspace-0.0.47/blackspace_compiler/ast/types/complex/string.py +12 -0
  61. blackspace-0.0.47/blackspace_compiler/ast/types/primitives/__init__.py +0 -0
  62. blackspace-0.0.47/blackspace_compiler/ast/types/primitives/bool.py +12 -0
  63. blackspace-0.0.47/blackspace_compiler/ast/types/primitives/int.py +12 -0
  64. blackspace-0.0.47/blackspace_compiler/ast/types/primitives/void.py +12 -0
  65. blackspace-0.0.47/blackspace_compiler/ast/types/type.py +9 -0
  66. blackspace-0.0.47/blackspace_compiler/ast/utils/__init__.py +0 -0
  67. blackspace-0.0.47/blackspace_compiler/ast/utils/error_message.py +7 -0
  68. blackspace-0.0.47/blackspace_compiler/ast/utils/indent.py +5 -0
  69. blackspace-0.0.47/blackspace_compiler/whitespace/__init__.py +0 -0
  70. blackspace-0.0.47/blackspace_compiler/whitespace/converters.py +21 -0
  71. blackspace-0.0.47/blackspace_compiler/whitespace/printstr.py +9 -0
  72. blackspace-0.0.47/blackspace_compiler/whitespace/snippets.py +31 -0
  73. blackspace-0.0.47/blackspace_compiler/whitespace/tokens.py +3 -0
  74. blackspace-0.0.47/pyproject.toml +50 -0
  75. 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,2 @@
1
+ [console_scripts]
2
+ blackspace = blackspace_compiler.__main__:main
@@ -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
@@ -0,0 +1,6 @@
1
+ def main() -> None:
2
+ pass
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
File without changes
@@ -0,0 +1,4 @@
1
+ from .evaluation_context import EvaluationContext
2
+ from .issue_level import IssueLevel
3
+
4
+ __all__ = ["EvaluationContext", "IssueLevel"]
@@ -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
@@ -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
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class IssueLevel(Enum):
5
+ WARNING = "warning"
6
+ ERROR = "error"
@@ -0,0 +1,8 @@
1
+ class LabelRegistry:
2
+ def __init__(self):
3
+ self._counter = 0
4
+
5
+ def new_label(self) -> int:
6
+ label = self._counter
7
+ self._counter += 1
8
+ return label
@@ -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(" ", "·")
@@ -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
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+
3
+ from ..types.type import Type
4
+
5
+
6
+ @dataclass
7
+ class VariableDefinition:
8
+ name: str
9
+ type: Type
10
+ is_mutable: bool
@@ -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