spamoji 0.1.0__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.
spamoji-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Philipp Dona
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
spamoji-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: spamoji
3
+ Version: 0.1.0
4
+ Summary: A programming language that only uses emojis as syntax.
5
+ Author: iqnite
6
+ License-Expression: MIT
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # spamoji
13
+
14
+ A programming language that only uses emojis as syntax and encourages writing spaghetti code.
15
+
16
+ ## Example
17
+
18
+ ```spamoji
19
+ πŸ—’οΈ A function to calculate factorial
20
+ βš™οΈ πŸ”’β•πŸ«ΈπŸ”°πŸ«·
21
+ πŸ‘‹ 🏁 🫴 1
22
+ πŸ”ƒ πŸ”° 🀜 1
23
+ 🏁 🫴 🏁 βœ–οΈ πŸ”°
24
+ πŸ”° 🫴 πŸ”° βž– 1
25
+ ↩️ 🏁
26
+
27
+ πŸ’­πŸ«ΈπŸ”€Enter a number to calculate the factorial: πŸ”€πŸ«·
28
+ πŸ‘‹ πŸ˜€ 🫴 πŸ”’πŸ«ΈβŒ¨οΈβ—πŸ«·
29
+ πŸ€” πŸ˜€ 🟰 ⚠️
30
+ πŸ‘
31
+ πŸ’¬πŸ«ΈπŸ”€Invalid input.πŸ”€πŸ«·
32
+ πŸ‘Ž
33
+ πŸ’¬πŸ«ΈπŸ”€The factorial of πŸ”€πŸ˜€πŸ”€ is πŸ”€πŸ«ΈπŸ”’β•πŸ«ΈπŸ˜€πŸ«·πŸ«·πŸ«·
34
+ ```
35
+
36
+ ## What's this all about?
37
+
38
+ This language is a learning project. Since it lacks many features of a typical programming language, it is not recommended for production use. If this experiment is successful, I might move on and begin working on Esore, a more serious language I've been designing in the past 2 years.
39
+
40
+ ## Installation
41
+
42
+ To install the spamoji interpreter, you will need to have Python 3.10 or higher installed on your machine. You can download Python from the [official website](https://www.python.org/downloads/).
43
+
44
+ Once you have Python installed, you can install the spamoji interpreter using pip:
45
+
46
+ ```bash
47
+ pip install spamoji
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ To run a spamoji program, you will need to use the spamoji interpreter.
53
+
54
+ You can provide a spamoji file (with the .🍝 extension) as input to run it. For example:
55
+
56
+ ```bash
57
+ spamoji my_program.🍝
58
+ ```
59
+
60
+ If no file is supplied, the interactive REPL will start, which allows to enter and evaluate expressions directly.
61
+
62
+ If the above command doesn't work, you can also run the interpreter using Python's -m flag:
63
+
64
+ ```bash
65
+ python -m spamoji my_program.🍝
66
+ ```
67
+
68
+ ## Syntax
69
+
70
+ > [!IMPORTANT]
71
+ > The emojis used in the syntax were entered on a Windows machine. While they should work on other platforms, some emojis might not render correctly or might be replaced with different emojis. If you encounter any issues, please try copying and pasting the emojis from this documentation into your code.
72
+
73
+ ### Statements
74
+
75
+ Each line of code starts with an emoji that indicates the type of statement. Each statement is followed by the relevant information, separated by spaces. Statements include:
76
+
77
+ - πŸ—’οΈ Comment: Text in this line will be ignored
78
+ - πŸ‘‹ Variable declaration: Followed by the variable assignment
79
+ - πŸ€” If statement: Followed by the condition
80
+ - πŸ‘ If true block
81
+ - πŸ‘Ž Else block
82
+ - πŸ”ƒ Loop: Followed by the loop variable and condition
83
+ - β›” Break statement: Used to exit the loop
84
+ - ‴️ Continue statement: Used to skip to the next iteration of the loop
85
+ - βš™οΈ Function definition: Followed by the function name and parameters
86
+ - ↩️ Return statement: Followed by the value to return
87
+ <!-- - 🧩 Import statement: Followed by a file name, it gets replaced by the contents of that file before execution -->
88
+
89
+ Some statements can be nested, such as if statements and loops. Indentation is used to indicate the scope of these statements. Indented blocks are considered part of the previous statement until the indentation level decreases. Variables declared inside of a block are only accessible inside of that block and any nested blocks.
90
+
91
+ ### Functions
92
+
93
+ Functions are defined using the βš™οΈ emoji, followed by the function name and parameters. The function body is indented and can contain any valid statements. The function can return a value using the ↩️ emoji.
94
+
95
+ Functions can be called by using their name followed by the arguments, separated by πŸ”Έ, between 🫸 and 🫷. Functions with no arguments can also be called with the ❗ emoji.
96
+
97
+ Here's an example of a function definition and call:
98
+
99
+ ```spamoji
100
+ βš™οΈ greetAndAsk 🫸firstName πŸ”Έ lastName🫷
101
+ πŸ’¬πŸ«ΈπŸ”€Hello, πŸ”€firstNameπŸ”€ πŸ”€lastNameπŸ”€! Please enter a number...πŸ”€πŸ«·
102
+ ↩️ βŒ¨οΈβ— πŸ—’οΈ Return the user input
103
+
104
+ greetAndAskπŸ«ΈπŸ”€JohnπŸ”€ πŸ”Έ πŸ”€DoeπŸ”€πŸ«·
105
+ ```
106
+
107
+ ### Variables
108
+
109
+ Variables are declared using the πŸ‘‹ emoji, followed by the variable assignment expression. An assinment expression starts with the variable name, followed by 🫴 and a value. Variable names can be any combination of letters, numbers, and emojis, but must start with a letter or emoji. Variables can be used in expressions and statements after they have been assigned.
110
+
111
+ ### Expressions
112
+
113
+ Many statements can be used as expressions within other statements. 🫸 and 🫷 can be used to group expressions and control the order of evaluation. For example, you can use a function call as part of an if statement condition or as part of a variable assignment.
114
+
115
+ ### Operators
116
+
117
+ The language supports basic operators for logical and arithmetic operations. These can be used between two values to perform calculations.
118
+
119
+ - βž•: Addition
120
+ - βž–: Subtraction
121
+ - βœ–οΈ: Multiplication
122
+ - βž—: Division
123
+ - 🟰: Equal
124
+ - πŸ†š: Not equal
125
+ - 🀜: Greater than
126
+ - πŸ€›: Less than
127
+ - 🀝: Logical AND
128
+ - 🀲: Logical OR
129
+ - πŸ™…: Logical NOT
130
+
131
+ <!-- Operators can be used directly after πŸ‘‹ in variable assignments to perform operations on the variable. For example, `πŸ‘‹βž• x 1` will increment the variable `x` by 1. -->
132
+
133
+ ### Strings
134
+
135
+ Strings are sequences of characters enclosed in πŸ”€ on each side. They can contain any characters, including emojis. Strings can be used in variable assignments, printed to the console, and concatenated using the βž• operator.
136
+
137
+ Values directly before or after a string will also be concatenated with that string.
138
+
139
+ ### Built-ins
140
+
141
+ The language includes several built-in functions and values for common operations, such as:
142
+
143
+ - πŸ’¬: Print output to a new line in the console
144
+ - πŸ’­: Print output to the console
145
+ - ⌨️: Get user input from the console
146
+ - ⏳: Wait for a specified number of seconds
147
+ - πŸ”’: Convert input to a number, return ⚠️ if the conversion fails
148
+ - 🎲: Get a random integer between 2 numbers
149
+ - πŸ›‘: Stop the program
150
+ - βœ…: A special value representing true
151
+ - ❌: A special value representing false
152
+ - πŸ«₯: A special value representing null
153
+ - ⚠️: A special value representing an error or undefined value
154
+ - 🐍: Evaluate a Python expression
155
+
156
+ ## Credits
157
+
158
+ Thanks to Robert Nystrom for his book "Crafting Interpreters" which inspired the design of this language and provided guidance on how to implement it.
159
+
160
+ Also thanks to Hack Club for providing a supportive community and motivating me to work on this project.
161
+
162
+ This documentation was entirely written by hand. AI tools were used for assistance while writing the interpreter.
@@ -0,0 +1,151 @@
1
+ # spamoji
2
+
3
+ A programming language that only uses emojis as syntax and encourages writing spaghetti code.
4
+
5
+ ## Example
6
+
7
+ ```spamoji
8
+ πŸ—’οΈ A function to calculate factorial
9
+ βš™οΈ πŸ”’β•πŸ«ΈπŸ”°πŸ«·
10
+ πŸ‘‹ 🏁 🫴 1
11
+ πŸ”ƒ πŸ”° 🀜 1
12
+ 🏁 🫴 🏁 βœ–οΈ πŸ”°
13
+ πŸ”° 🫴 πŸ”° βž– 1
14
+ ↩️ 🏁
15
+
16
+ πŸ’­πŸ«ΈπŸ”€Enter a number to calculate the factorial: πŸ”€πŸ«·
17
+ πŸ‘‹ πŸ˜€ 🫴 πŸ”’πŸ«ΈβŒ¨οΈβ—πŸ«·
18
+ πŸ€” πŸ˜€ 🟰 ⚠️
19
+ πŸ‘
20
+ πŸ’¬πŸ«ΈπŸ”€Invalid input.πŸ”€πŸ«·
21
+ πŸ‘Ž
22
+ πŸ’¬πŸ«ΈπŸ”€The factorial of πŸ”€πŸ˜€πŸ”€ is πŸ”€πŸ«ΈπŸ”’β•πŸ«ΈπŸ˜€πŸ«·πŸ«·πŸ«·
23
+ ```
24
+
25
+ ## What's this all about?
26
+
27
+ This language is a learning project. Since it lacks many features of a typical programming language, it is not recommended for production use. If this experiment is successful, I might move on and begin working on Esore, a more serious language I've been designing in the past 2 years.
28
+
29
+ ## Installation
30
+
31
+ To install the spamoji interpreter, you will need to have Python 3.10 or higher installed on your machine. You can download Python from the [official website](https://www.python.org/downloads/).
32
+
33
+ Once you have Python installed, you can install the spamoji interpreter using pip:
34
+
35
+ ```bash
36
+ pip install spamoji
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ To run a spamoji program, you will need to use the spamoji interpreter.
42
+
43
+ You can provide a spamoji file (with the .🍝 extension) as input to run it. For example:
44
+
45
+ ```bash
46
+ spamoji my_program.🍝
47
+ ```
48
+
49
+ If no file is supplied, the interactive REPL will start, which allows to enter and evaluate expressions directly.
50
+
51
+ If the above command doesn't work, you can also run the interpreter using Python's -m flag:
52
+
53
+ ```bash
54
+ python -m spamoji my_program.🍝
55
+ ```
56
+
57
+ ## Syntax
58
+
59
+ > [!IMPORTANT]
60
+ > The emojis used in the syntax were entered on a Windows machine. While they should work on other platforms, some emojis might not render correctly or might be replaced with different emojis. If you encounter any issues, please try copying and pasting the emojis from this documentation into your code.
61
+
62
+ ### Statements
63
+
64
+ Each line of code starts with an emoji that indicates the type of statement. Each statement is followed by the relevant information, separated by spaces. Statements include:
65
+
66
+ - πŸ—’οΈ Comment: Text in this line will be ignored
67
+ - πŸ‘‹ Variable declaration: Followed by the variable assignment
68
+ - πŸ€” If statement: Followed by the condition
69
+ - πŸ‘ If true block
70
+ - πŸ‘Ž Else block
71
+ - πŸ”ƒ Loop: Followed by the loop variable and condition
72
+ - β›” Break statement: Used to exit the loop
73
+ - ‴️ Continue statement: Used to skip to the next iteration of the loop
74
+ - βš™οΈ Function definition: Followed by the function name and parameters
75
+ - ↩️ Return statement: Followed by the value to return
76
+ <!-- - 🧩 Import statement: Followed by a file name, it gets replaced by the contents of that file before execution -->
77
+
78
+ Some statements can be nested, such as if statements and loops. Indentation is used to indicate the scope of these statements. Indented blocks are considered part of the previous statement until the indentation level decreases. Variables declared inside of a block are only accessible inside of that block and any nested blocks.
79
+
80
+ ### Functions
81
+
82
+ Functions are defined using the βš™οΈ emoji, followed by the function name and parameters. The function body is indented and can contain any valid statements. The function can return a value using the ↩️ emoji.
83
+
84
+ Functions can be called by using their name followed by the arguments, separated by πŸ”Έ, between 🫸 and 🫷. Functions with no arguments can also be called with the ❗ emoji.
85
+
86
+ Here's an example of a function definition and call:
87
+
88
+ ```spamoji
89
+ βš™οΈ greetAndAsk 🫸firstName πŸ”Έ lastName🫷
90
+ πŸ’¬πŸ«ΈπŸ”€Hello, πŸ”€firstNameπŸ”€ πŸ”€lastNameπŸ”€! Please enter a number...πŸ”€πŸ«·
91
+ ↩️ βŒ¨οΈβ— πŸ—’οΈ Return the user input
92
+
93
+ greetAndAskπŸ«ΈπŸ”€JohnπŸ”€ πŸ”Έ πŸ”€DoeπŸ”€πŸ«·
94
+ ```
95
+
96
+ ### Variables
97
+
98
+ Variables are declared using the πŸ‘‹ emoji, followed by the variable assignment expression. An assinment expression starts with the variable name, followed by 🫴 and a value. Variable names can be any combination of letters, numbers, and emojis, but must start with a letter or emoji. Variables can be used in expressions and statements after they have been assigned.
99
+
100
+ ### Expressions
101
+
102
+ Many statements can be used as expressions within other statements. 🫸 and 🫷 can be used to group expressions and control the order of evaluation. For example, you can use a function call as part of an if statement condition or as part of a variable assignment.
103
+
104
+ ### Operators
105
+
106
+ The language supports basic operators for logical and arithmetic operations. These can be used between two values to perform calculations.
107
+
108
+ - βž•: Addition
109
+ - βž–: Subtraction
110
+ - βœ–οΈ: Multiplication
111
+ - βž—: Division
112
+ - 🟰: Equal
113
+ - πŸ†š: Not equal
114
+ - 🀜: Greater than
115
+ - πŸ€›: Less than
116
+ - 🀝: Logical AND
117
+ - 🀲: Logical OR
118
+ - πŸ™…: Logical NOT
119
+
120
+ <!-- Operators can be used directly after πŸ‘‹ in variable assignments to perform operations on the variable. For example, `πŸ‘‹βž• x 1` will increment the variable `x` by 1. -->
121
+
122
+ ### Strings
123
+
124
+ Strings are sequences of characters enclosed in πŸ”€ on each side. They can contain any characters, including emojis. Strings can be used in variable assignments, printed to the console, and concatenated using the βž• operator.
125
+
126
+ Values directly before or after a string will also be concatenated with that string.
127
+
128
+ ### Built-ins
129
+
130
+ The language includes several built-in functions and values for common operations, such as:
131
+
132
+ - πŸ’¬: Print output to a new line in the console
133
+ - πŸ’­: Print output to the console
134
+ - ⌨️: Get user input from the console
135
+ - ⏳: Wait for a specified number of seconds
136
+ - πŸ”’: Convert input to a number, return ⚠️ if the conversion fails
137
+ - 🎲: Get a random integer between 2 numbers
138
+ - πŸ›‘: Stop the program
139
+ - βœ…: A special value representing true
140
+ - ❌: A special value representing false
141
+ - πŸ«₯: A special value representing null
142
+ - ⚠️: A special value representing an error or undefined value
143
+ - 🐍: Evaluate a Python expression
144
+
145
+ ## Credits
146
+
147
+ Thanks to Robert Nystrom for his book "Crafting Interpreters" which inspired the design of this language and provided guidance on how to implement it.
148
+
149
+ Also thanks to Hack Club for providing a supportive community and motivating me to work on this project.
150
+
151
+ This documentation was entirely written by hand. AI tools were used for assistance while writing the interpreter.
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "spamoji"
7
+ version = "0.1.0"
8
+ description = "A programming language that only uses emojis as syntax."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [{ name = "iqnite" }]
13
+ dependencies = []
14
+
15
+ [project.scripts]
16
+ spamoji = "spamoji.spamoji:main"
17
+
18
+ [tool.setuptools]
19
+ packages = ["spamoji"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """
2
+ Interface for the Spamoji interpreter.
3
+ """
4
+
5
+ from spamoji.spamoji import Spamoji, main
@@ -0,0 +1,34 @@
1
+ """
2
+ Contains classes and functions for variable environments.
3
+ """
4
+
5
+ from spamoji.helpers import SpamojiRuntimeError
6
+ from spamoji.token import Token
7
+
8
+
9
+ class Environment:
10
+ values: dict[str, object]
11
+ enclosing: "Environment | None"
12
+
13
+ def __init__(self, enclosing: "Environment | None" = None):
14
+ self.values = {}
15
+ self.enclosing = enclosing
16
+
17
+ def define(self, name: str, value: object):
18
+ self.values[name] = value
19
+
20
+ def assign(self, name: Token, value: object):
21
+ if name.lexeme in self.values:
22
+ self.values[name.lexeme] = value
23
+ return
24
+ if self.enclosing is not None:
25
+ self.enclosing.assign(name, value)
26
+ return
27
+ raise SpamojiRuntimeError(name, "Undefined variable '" + name.lexeme + "'.")
28
+
29
+ def get(self, name: Token) -> object:
30
+ if name.lexeme in self.values:
31
+ return self.values[name.lexeme]
32
+ if self.enclosing is not None:
33
+ return self.enclosing.get(name)
34
+ raise SpamojiRuntimeError(name, f"Undefined variable '{name.lexeme}'.")
@@ -0,0 +1,134 @@
1
+ """
2
+ This module contains the Expr class and its subclasses.
3
+ Generated by generate_ast.py
4
+ """
5
+
6
+ from abc import ABC
7
+ from spamoji.token import Token
8
+
9
+
10
+ class Expr(ABC):
11
+ """Base class for all exprs."""
12
+ def accept(self, visitor: "Visitor") -> object:
13
+ pass
14
+
15
+
16
+ class Visitor(ABC):
17
+ def visit_assign_expr(self, expr: "Assign") -> object:
18
+ pass
19
+ def visit_binary_expr(self, expr: "Binary") -> object:
20
+ pass
21
+ def visit_call_expr(self, expr: "Call") -> object:
22
+ pass
23
+ def visit_logical_expr(self, expr: "Logical") -> object:
24
+ pass
25
+ def visit_if_expr(self, expr: "If") -> object:
26
+ pass
27
+ def visit_grouping_expr(self, expr: "Grouping") -> object:
28
+ pass
29
+ def visit_literal_expr(self, expr: "Literal") -> object:
30
+ pass
31
+ def visit_unary_expr(self, expr: "Unary") -> object:
32
+ pass
33
+ def visit_variable_expr(self, expr: "Variable") -> object:
34
+ pass
35
+
36
+
37
+ class Assign(Expr):
38
+ """Represents a assign expression."""
39
+
40
+ def __init__(self, name: Token, value: Expr):
41
+ self.name = name
42
+ self.value = value
43
+
44
+ def accept(self, visitor: Visitor) -> object:
45
+ return visitor.visit_assign_expr(self)
46
+
47
+
48
+ class Binary(Expr):
49
+ """Represents a binary expression."""
50
+
51
+ def __init__(self, left: Expr, operator: Token, right: Expr):
52
+ self.left = left
53
+ self.operator = operator
54
+ self.right = right
55
+
56
+ def accept(self, visitor: Visitor) -> object:
57
+ return visitor.visit_binary_expr(self)
58
+
59
+
60
+ class Call(Expr):
61
+ """Represents a call expression."""
62
+
63
+ def __init__(self, callee: Expr, paren: Token, arguments: list[Expr]):
64
+ self.callee = callee
65
+ self.paren = paren
66
+ self.arguments = arguments
67
+
68
+ def accept(self, visitor: Visitor) -> object:
69
+ return visitor.visit_call_expr(self)
70
+
71
+
72
+ class Logical(Expr):
73
+ """Represents a logical expression."""
74
+
75
+ def __init__(self, left: Expr, operator: Token, right: Expr):
76
+ self.left = left
77
+ self.operator = operator
78
+ self.right = right
79
+
80
+ def accept(self, visitor: Visitor) -> object:
81
+ return visitor.visit_logical_expr(self)
82
+
83
+
84
+ class If(Expr):
85
+ """Represents a if expression."""
86
+
87
+ def __init__(self, condition: Expr, then_branch: Expr, else_branch: Expr):
88
+ self.condition = condition
89
+ self.then_branch = then_branch
90
+ self.else_branch = else_branch
91
+
92
+ def accept(self, visitor: Visitor) -> object:
93
+ return visitor.visit_if_expr(self)
94
+
95
+
96
+ class Grouping(Expr):
97
+ """Represents a grouping expression."""
98
+
99
+ def __init__(self, expression: Expr):
100
+ self.expression = expression
101
+
102
+ def accept(self, visitor: Visitor) -> object:
103
+ return visitor.visit_grouping_expr(self)
104
+
105
+
106
+ class Literal(Expr):
107
+ """Represents a literal expression."""
108
+
109
+ def __init__(self, value: object):
110
+ self.value = value
111
+
112
+ def accept(self, visitor: Visitor) -> object:
113
+ return visitor.visit_literal_expr(self)
114
+
115
+
116
+ class Unary(Expr):
117
+ """Represents a unary expression."""
118
+
119
+ def __init__(self, operator: Token, right: Expr):
120
+ self.operator = operator
121
+ self.right = right
122
+
123
+ def accept(self, visitor: Visitor) -> object:
124
+ return visitor.visit_unary_expr(self)
125
+
126
+
127
+ class Variable(Expr):
128
+ """Represents a variable expression."""
129
+
130
+ def __init__(self, name: Token):
131
+ self.name = name
132
+
133
+ def accept(self, visitor: Visitor) -> object:
134
+ return visitor.visit_variable_expr(self)
@@ -0,0 +1,50 @@
1
+ """
2
+ Contains classes for functions and callables.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from spamoji import stmt
8
+ from spamoji.environment import Environment
9
+
10
+ if TYPE_CHECKING:
11
+ from spamoji.interpreter import Interpreter
12
+
13
+
14
+ class SpamojiCallable:
15
+ def call(self, interpreter: "Interpreter", arguments: list[object]) -> object: ...
16
+ def arity(self) -> int: ...
17
+
18
+
19
+ class SpamojiFunction(SpamojiCallable):
20
+ def __init__(self, declaration: stmt.Function):
21
+ self.declaration = declaration
22
+
23
+ def call(self, interpreter: "Interpreter", arguments: list[object]) -> object:
24
+ environment = Environment(interpreter.globals)
25
+ for i, arg in enumerate(self.declaration.arguments):
26
+ environment.define(arg.lexeme, arguments[i])
27
+ try:
28
+ interpreter.execute_block(self.declaration.body, environment)
29
+ except Return as return_value:
30
+ return return_value.value
31
+
32
+ def arity(self) -> int:
33
+ return len(self.declaration.arguments)
34
+
35
+ def __str__(self) -> str:
36
+ return f"<βš™οΈ {self.declaration.name.lexeme}>"
37
+
38
+
39
+ class Return(RuntimeError):
40
+ def __init__(self, value: object, *_):
41
+ super().__init__()
42
+ self.value = value
43
+
44
+
45
+ class BreakLoop(RuntimeError):
46
+ pass
47
+
48
+
49
+ class ContinueLoop(RuntimeError):
50
+ pass
@@ -0,0 +1,61 @@
1
+ """
2
+ Contains helper functions and classes for the Spamoji interpreter.
3
+ """
4
+
5
+ import typing
6
+
7
+ from spamoji.expr import Binary, Expr, Grouping, Literal, Unary, Visitor
8
+ from spamoji.token import Token, TokenType
9
+
10
+
11
+ def error_token(
12
+ token: Token,
13
+ message: str,
14
+ error_handler: typing.Callable[[int, str, str], typing.Any],
15
+ ):
16
+ """Reports an error with a given message and token."""
17
+ if token.token_type == TokenType.EOF:
18
+ error_handler(token.line, " at end", message)
19
+ else:
20
+ error_handler(token.line, f" at '{token.lexeme}'", message)
21
+
22
+
23
+ class ASTPrinter(Visitor):
24
+ """Prints an AST in a human-readable format."""
25
+
26
+ def print(self, expr: Expr) -> str:
27
+ """Prints an AST in a human-readable format."""
28
+ return f"{expr.accept(self)}"
29
+
30
+ def visit_binary_expr(self, expr: Binary) -> str:
31
+ return self.parenthesize(expr.operator.lexeme, expr.left, expr.right)
32
+
33
+ def visit_grouping_expr(self, expr: Grouping) -> str:
34
+ return self.parenthesize("group", expr.expression)
35
+
36
+ def visit_literal_expr(self, expr: Literal) -> str:
37
+ if expr.value is None:
38
+ return "nil"
39
+ return str(expr.value)
40
+
41
+ def visit_unary_expr(self, expr: Unary) -> str:
42
+ return self.parenthesize(expr.operator.lexeme, expr.right)
43
+
44
+ def parenthesize(self, name: str, *exprs: Expr) -> str:
45
+ result = f"({name}"
46
+ for expr in exprs:
47
+ result += f" {expr.accept(self)}"
48
+ result += ")"
49
+ return result
50
+
51
+
52
+ class SpamojiRuntimeError(RuntimeError):
53
+ def __init__(self, token: Token, message: str, *args: object):
54
+ super().__init__(message, *args)
55
+ self.token = token
56
+
57
+
58
+ class SpamojiValueError: ...
59
+
60
+
61
+ spamojiValueError = SpamojiValueError()