brasa-lang 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.
- brasa_lang-0.1.0/PKG-INFO +6 -0
- brasa_lang-0.1.0/README.md +3 -0
- brasa_lang-0.1.0/brasa/__init__.py +1 -0
- brasa_lang-0.1.0/brasa/cli.py +14 -0
- brasa_lang-0.1.0/brasa/core/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/core/nodes/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/core/nodes/basics.py +13 -0
- brasa_lang-0.1.0/brasa/core/nodes/operators.py +14 -0
- brasa_lang-0.1.0/brasa/core/nodes/statements.py +85 -0
- brasa_lang-0.1.0/brasa/core/nodes/types.py +39 -0
- brasa_lang-0.1.0/brasa/core/nodes/values.py +36 -0
- brasa_lang-0.1.0/brasa/core/types/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/core/types/lvalues.py +33 -0
- brasa_lang-0.1.0/brasa/core/types/operators.py +21 -0
- brasa_lang-0.1.0/brasa/interpreter/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/interpreter/interpreter.py +301 -0
- brasa_lang-0.1.0/brasa/interpreter/signals.py +13 -0
- brasa_lang-0.1.0/brasa/parser/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/parser/ast_builder.py +300 -0
- brasa_lang-0.1.0/brasa/repl.py +52 -0
- brasa_lang-0.1.0/brasa/runner.py +48 -0
- brasa_lang-0.1.0/brasa/runtime/__init__.py +0 -0
- brasa_lang-0.1.0/brasa/runtime/scope.py +21 -0
- brasa_lang-0.1.0/brasa/runtime/world.py +35 -0
- brasa_lang-0.1.0/brasa_lang.egg-info/PKG-INFO +6 -0
- brasa_lang-0.1.0/brasa_lang.egg-info/SOURCES.txt +29 -0
- brasa_lang-0.1.0/brasa_lang.egg-info/dependency_links.txt +1 -0
- brasa_lang-0.1.0/brasa_lang.egg-info/entry_points.txt +2 -0
- brasa_lang-0.1.0/brasa_lang.egg-info/top_level.txt +1 -0
- brasa_lang-0.1.0/pyproject.toml +12 -0
- brasa_lang-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from brasa.core.types.operators import BinaryOperationEnum,UnaryOperationEnum
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class BinaryOperation:
|
|
7
|
+
left:any
|
|
8
|
+
op:BinaryOperationEnum
|
|
9
|
+
right:any
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class UnaryOperation:
|
|
13
|
+
op:UnaryOperationEnum
|
|
14
|
+
expr:any
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from brasa.core.nodes.types import Type
|
|
4
|
+
from brasa.core.nodes.values import Value
|
|
5
|
+
|
|
6
|
+
# ---------------- VARIABLES ----------------
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class VariableDeclarationStatement:
|
|
10
|
+
id:str
|
|
11
|
+
type:Type
|
|
12
|
+
expr:Value
|
|
13
|
+
is_const:bool
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class AssignmentStatement:
|
|
17
|
+
target:str
|
|
18
|
+
expr:Value
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class CompoundAssignmentStatement:
|
|
22
|
+
target:any
|
|
23
|
+
op:any
|
|
24
|
+
expr:any
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class PostfixStatement:
|
|
28
|
+
target:any
|
|
29
|
+
op:any
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class IndexExpression:
|
|
33
|
+
base:any
|
|
34
|
+
index:any
|
|
35
|
+
|
|
36
|
+
# ---------------- CONTROL FLOW ----------------
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class IfStatement:
|
|
40
|
+
condition:any
|
|
41
|
+
then_block:any
|
|
42
|
+
else_branch:any
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class WhileStatement:
|
|
46
|
+
condition:any
|
|
47
|
+
body:any
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class BreakStatement:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class ContinueStatement:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
# ---------------- FUNCTIONS ----------------
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class FunctionDeclaration:
|
|
61
|
+
name:str
|
|
62
|
+
params:any
|
|
63
|
+
return_type:any
|
|
64
|
+
body:any
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class LambdaExpression:
|
|
68
|
+
params:any
|
|
69
|
+
return_type:any
|
|
70
|
+
body:any
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class CallExpression:
|
|
74
|
+
callee:any
|
|
75
|
+
args:any
|
|
76
|
+
|
|
77
|
+
@dataclass
|
|
78
|
+
class ReturnStatement:
|
|
79
|
+
expr:any
|
|
80
|
+
|
|
81
|
+
# ---------------- REMOVE LATER ----------------
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class PrintStatement:
|
|
85
|
+
expr:Value
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
@dataclass
|
|
4
|
+
class Type:
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class IntegerType(Type):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class FloatType(Type):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class StringType:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class NullableType(Type):
|
|
21
|
+
base_type:Type
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class BooleanType(Type):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ArrayType(Type):
|
|
29
|
+
element_type:Type
|
|
30
|
+
size:int
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class VoidType(Type):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class FunctionType(Type):
|
|
38
|
+
param_types:list[Type]
|
|
39
|
+
return_type:Type
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
@dataclass
|
|
4
|
+
class Value:
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class IntegerValue(Value):
|
|
9
|
+
value:int
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class FloatValue(Value):
|
|
13
|
+
value:float
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class StringLiteral:
|
|
17
|
+
value:str
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class NullValue(Value):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class BooleanValue(Value):
|
|
25
|
+
value:bool
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ArrayValue(Value):
|
|
29
|
+
elements:list[Value]
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class FunctionValue:
|
|
33
|
+
params:any
|
|
34
|
+
body:any
|
|
35
|
+
return_type:any
|
|
36
|
+
closure_scope:any
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class LValue:
|
|
2
|
+
def get(self,interpreter):
|
|
3
|
+
raise NotImplementedError
|
|
4
|
+
|
|
5
|
+
def set(self,interpreter,value):
|
|
6
|
+
raise NotImplementedError
|
|
7
|
+
|
|
8
|
+
class VarLValue(LValue):
|
|
9
|
+
def __init__(self,name):
|
|
10
|
+
self.name = name
|
|
11
|
+
|
|
12
|
+
def get(self,interpreter):
|
|
13
|
+
var_id = interpreter.current_scope.lookup(self.name)
|
|
14
|
+
return interpreter.world.get_value(var_id)
|
|
15
|
+
|
|
16
|
+
def set(self,interpreter,value):
|
|
17
|
+
var_id = interpreter.current_scope.lookup(self.name)
|
|
18
|
+
interpreter.world.set_value(var_id,value)
|
|
19
|
+
|
|
20
|
+
class IndexLValue(LValue):
|
|
21
|
+
def __init__(self,base,index_expr):
|
|
22
|
+
self.base=base
|
|
23
|
+
self.index_expr=index_expr
|
|
24
|
+
|
|
25
|
+
def get(self,interpreter):
|
|
26
|
+
arr=self.base.get(interpreter)
|
|
27
|
+
index=interpreter.visit(self.index_expr)
|
|
28
|
+
return arr[index]
|
|
29
|
+
|
|
30
|
+
def set(self,interpreter,value):
|
|
31
|
+
arr=self.base.get(interpreter)
|
|
32
|
+
index=interpreter.visit(self.index_expr)
|
|
33
|
+
arr[index]=value
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
|
|
3
|
+
class BinaryOperationEnum(Enum):
|
|
4
|
+
ADD = auto()
|
|
5
|
+
SUB = auto()
|
|
6
|
+
MUL = auto()
|
|
7
|
+
DIV = auto()
|
|
8
|
+
|
|
9
|
+
GT = auto()
|
|
10
|
+
LT = auto()
|
|
11
|
+
GE = auto()
|
|
12
|
+
LE = auto()
|
|
13
|
+
EQ = auto()
|
|
14
|
+
NE = auto()
|
|
15
|
+
|
|
16
|
+
AND = auto()
|
|
17
|
+
OR = auto()
|
|
18
|
+
|
|
19
|
+
class UnaryOperationEnum(Enum):
|
|
20
|
+
NEG = auto()
|
|
21
|
+
NOT = auto()
|
|
File without changes
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from brasa.runtime.scope import Scope
|
|
2
|
+
from brasa.runtime.world import World
|
|
3
|
+
|
|
4
|
+
from brasa.core.nodes.values import FunctionValue
|
|
5
|
+
from brasa.core.types.operators import BinaryOperationEnum,UnaryOperationEnum
|
|
6
|
+
|
|
7
|
+
from brasa.interpreter.signals import BreakSignal,ContinueSignal,ReturnSignal
|
|
8
|
+
|
|
9
|
+
class Interpreter:
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.current_scope=Scope()
|
|
12
|
+
self.world=World()
|
|
13
|
+
|
|
14
|
+
def visit(self,node):
|
|
15
|
+
method_name=f'visit_{type(node).__name__}'
|
|
16
|
+
visitor=getattr(
|
|
17
|
+
self,
|
|
18
|
+
method_name,
|
|
19
|
+
self.generic_visit
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
return visitor(node)
|
|
23
|
+
|
|
24
|
+
def generic_visit(self, node):
|
|
25
|
+
raise Exception(f'Method visit_{type(node).__name__} does not exist.')
|
|
26
|
+
|
|
27
|
+
# ---------------- BASICS ----------------
|
|
28
|
+
|
|
29
|
+
def visit_Program(self,node):
|
|
30
|
+
for statement in node.statements:
|
|
31
|
+
self.visit(statement)
|
|
32
|
+
|
|
33
|
+
def visit_Block(self, node):
|
|
34
|
+
old_scope = self.current_scope
|
|
35
|
+
self.current_scope = Scope(parent=old_scope)
|
|
36
|
+
|
|
37
|
+
for stmt in node.statements:
|
|
38
|
+
self.visit(stmt)
|
|
39
|
+
|
|
40
|
+
self.current_scope = old_scope
|
|
41
|
+
|
|
42
|
+
# ---------------- VARIABLES ----------------
|
|
43
|
+
|
|
44
|
+
def visit_Identifier(self, node):
|
|
45
|
+
var_id = self.current_scope.lookup(node.name)
|
|
46
|
+
return self.world.get_value(var_id)
|
|
47
|
+
|
|
48
|
+
def visit_VariableDeclarationStatement(self,node):
|
|
49
|
+
value = self.visit(node.expr) if node.expr is not None else None
|
|
50
|
+
|
|
51
|
+
var_id=self.world.create(
|
|
52
|
+
type_=node.type,
|
|
53
|
+
value=value,
|
|
54
|
+
is_const=node.is_const
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
self.current_scope.declare(node.id.name,var_id)
|
|
58
|
+
|
|
59
|
+
def visit_AssignmentStatement(self,node):
|
|
60
|
+
# var_id = self.current_scope.lookup(node.id.name)
|
|
61
|
+
|
|
62
|
+
# if self.world.is_const(var_id):
|
|
63
|
+
# raise Exception(f"Cannot assign to const '{node.id.name}'")
|
|
64
|
+
|
|
65
|
+
# value = self.visit(node.expr)
|
|
66
|
+
# self.world.set_value(var_id, value)
|
|
67
|
+
value = self.visit(node.expr)
|
|
68
|
+
node.target.set(self, value)
|
|
69
|
+
|
|
70
|
+
def visit_CompoundAssignmentStatement(self, node):
|
|
71
|
+
# var_id = self.current_scope.lookup(node.id.name)
|
|
72
|
+
# current=self.world.get_value(var_id)
|
|
73
|
+
# value = self.visit(node.expr)
|
|
74
|
+
|
|
75
|
+
# if node.op == BinaryOperationEnum.ADD:
|
|
76
|
+
# result = current + value
|
|
77
|
+
# elif node.op == BinaryOperationEnum.SUB:
|
|
78
|
+
# result = current - value
|
|
79
|
+
# elif node.op == BinaryOperationEnum.MUL:
|
|
80
|
+
# result = current * value
|
|
81
|
+
# elif node.op == BinaryOperationEnum.DIV:
|
|
82
|
+
# result = current / value
|
|
83
|
+
|
|
84
|
+
# self.world.set_value(var_id,result)
|
|
85
|
+
current = node.target.get(self)
|
|
86
|
+
value = self.visit(node.expr)
|
|
87
|
+
|
|
88
|
+
if node.op==BinaryOperationEnum.ADD:
|
|
89
|
+
result=current+value
|
|
90
|
+
elif node.op==BinaryOperationEnum.SUB:
|
|
91
|
+
result=current-value
|
|
92
|
+
elif node.op==BinaryOperationEnum.MUL:
|
|
93
|
+
result= current*value
|
|
94
|
+
elif node.op==BinaryOperationEnum.DIV:
|
|
95
|
+
result=current/value
|
|
96
|
+
|
|
97
|
+
node.target.set(self,result)
|
|
98
|
+
|
|
99
|
+
def visit_PostfixStatement(self,node):
|
|
100
|
+
# var_id = self.current_scope.lookup(node.id.name)
|
|
101
|
+
# current=self.world.get_value(var_id)
|
|
102
|
+
|
|
103
|
+
# if node.op==BinaryOperationEnum.ADD:
|
|
104
|
+
# self.world.set_value(var_id,current+1)
|
|
105
|
+
# else:
|
|
106
|
+
# self.world.set_value(var_id,current-1)
|
|
107
|
+
current=node.target.get(self)
|
|
108
|
+
|
|
109
|
+
if node.op==BinaryOperationEnum.ADD:
|
|
110
|
+
new_value=current+1
|
|
111
|
+
else:
|
|
112
|
+
new_value=current-1
|
|
113
|
+
|
|
114
|
+
node.target.set(self,new_value)
|
|
115
|
+
|
|
116
|
+
return current
|
|
117
|
+
|
|
118
|
+
# ---------------- VALUES ----------------
|
|
119
|
+
|
|
120
|
+
def visit_IntegerValue(self,node):
|
|
121
|
+
return node.value
|
|
122
|
+
|
|
123
|
+
def visit_FloatValue(self,node):
|
|
124
|
+
return node.value
|
|
125
|
+
|
|
126
|
+
def visit_StringLiteral(self,node):
|
|
127
|
+
return node.value
|
|
128
|
+
|
|
129
|
+
def visit_NullValue(self,node):
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
def visit_BooleanValue(self,node):
|
|
133
|
+
return node.value
|
|
134
|
+
|
|
135
|
+
def visit_ArrayValue(self,node):
|
|
136
|
+
return [self.visit(elem) for elem in node.elements]
|
|
137
|
+
|
|
138
|
+
def visit_IndexExpression(self, node):
|
|
139
|
+
arr = self.visit(node.base)
|
|
140
|
+
index = self.visit(node.index)
|
|
141
|
+
return arr[index]
|
|
142
|
+
|
|
143
|
+
# ---------------- OPERATORS ----------------
|
|
144
|
+
|
|
145
|
+
def visit_BinaryOperation(self, node):
|
|
146
|
+
op = node.op
|
|
147
|
+
|
|
148
|
+
if op == BinaryOperationEnum.AND:
|
|
149
|
+
left=self.visit(node.left)
|
|
150
|
+
|
|
151
|
+
if not left:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
right=self.visit(node.right)
|
|
155
|
+
return bool(right)
|
|
156
|
+
|
|
157
|
+
if node.op == BinaryOperationEnum.OR:
|
|
158
|
+
left = self.visit(node.left)
|
|
159
|
+
|
|
160
|
+
if left:
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
right = self.visit(node.right)
|
|
164
|
+
return bool(right)
|
|
165
|
+
|
|
166
|
+
left = self.visit(node.left)
|
|
167
|
+
right = self.visit(node.right)
|
|
168
|
+
|
|
169
|
+
if op == BinaryOperationEnum.ADD:
|
|
170
|
+
return left + right
|
|
171
|
+
if op == BinaryOperationEnum.SUB:
|
|
172
|
+
return left - right
|
|
173
|
+
if op == BinaryOperationEnum.MUL:
|
|
174
|
+
return left * right
|
|
175
|
+
if op == BinaryOperationEnum.DIV:
|
|
176
|
+
return left / right
|
|
177
|
+
|
|
178
|
+
if op == BinaryOperationEnum.GT:
|
|
179
|
+
return left > right
|
|
180
|
+
if op == BinaryOperationEnum.LT:
|
|
181
|
+
return left < right
|
|
182
|
+
if op == BinaryOperationEnum.GE:
|
|
183
|
+
return left >= right
|
|
184
|
+
if op == BinaryOperationEnum.LE:
|
|
185
|
+
return left <= right
|
|
186
|
+
if op == BinaryOperationEnum.EQ:
|
|
187
|
+
return left == right
|
|
188
|
+
if op == BinaryOperationEnum.NE:
|
|
189
|
+
return left != right
|
|
190
|
+
|
|
191
|
+
raise Exception(f"Unknown operator: {op}")
|
|
192
|
+
|
|
193
|
+
def visit_UnaryOperation(self, node):
|
|
194
|
+
value = self.visit(node.expr)
|
|
195
|
+
|
|
196
|
+
if node.op == UnaryOperationEnum.NEG:
|
|
197
|
+
return -value
|
|
198
|
+
|
|
199
|
+
if node.op == UnaryOperationEnum.NOT:
|
|
200
|
+
return not value
|
|
201
|
+
|
|
202
|
+
raise Exception(f"Unknown unary operator: {node.op}")
|
|
203
|
+
|
|
204
|
+
# ---------------- CONTROL FLOW ----------------
|
|
205
|
+
|
|
206
|
+
def visit_IfStatement(self, node):
|
|
207
|
+
condition = self.visit(node.condition)
|
|
208
|
+
|
|
209
|
+
if condition:
|
|
210
|
+
return self.visit(node.then_block)
|
|
211
|
+
|
|
212
|
+
if node.else_branch:
|
|
213
|
+
return self.visit(node.else_branch)
|
|
214
|
+
|
|
215
|
+
def visit_WhileStatement(self, node):
|
|
216
|
+
while self.visit(node.condition):
|
|
217
|
+
try:
|
|
218
|
+
self.visit(node.body)
|
|
219
|
+
|
|
220
|
+
except ContinueSignal:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
except BreakSignal:
|
|
224
|
+
break
|
|
225
|
+
|
|
226
|
+
def visit_BreakStatement(self, node):
|
|
227
|
+
raise BreakSignal()
|
|
228
|
+
|
|
229
|
+
def visit_ContinueStatement(self, node):
|
|
230
|
+
raise ContinueSignal()
|
|
231
|
+
|
|
232
|
+
# ---------------- FUNCTIONS ----------------
|
|
233
|
+
|
|
234
|
+
def visit_FunctionDeclaration(self, node):
|
|
235
|
+
func=FunctionValue(
|
|
236
|
+
params=node.params,
|
|
237
|
+
body=node.body,
|
|
238
|
+
return_type=node.return_type,
|
|
239
|
+
closure_scope=self.current_scope
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
var_id=self.world.create(
|
|
243
|
+
type_=None,
|
|
244
|
+
value=func,
|
|
245
|
+
is_const=True
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
self.current_scope.declare(node.name.name, var_id)
|
|
249
|
+
|
|
250
|
+
def visit_LambdaExpression(self, node):
|
|
251
|
+
return FunctionValue(
|
|
252
|
+
params=node.params,
|
|
253
|
+
body=node.body,
|
|
254
|
+
return_type=node.return_type,
|
|
255
|
+
closure_scope=self.current_scope,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def visit_CallExpression(self, node):
|
|
259
|
+
func=self.visit(node.callee)
|
|
260
|
+
args=[self.visit(arg) for arg in node.args]
|
|
261
|
+
|
|
262
|
+
new_scope=Scope(parent=func.closure_scope)
|
|
263
|
+
|
|
264
|
+
for param, arg in zip(func.params, args):
|
|
265
|
+
var_id = self.world.create(type_=param[0], value=arg)
|
|
266
|
+
new_scope.declare(param[1].name, var_id)
|
|
267
|
+
|
|
268
|
+
old_scope=self.current_scope
|
|
269
|
+
self.current_scope=new_scope
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
self.visit(func.body)
|
|
273
|
+
result=None
|
|
274
|
+
except ReturnSignal as r:
|
|
275
|
+
result=r.value
|
|
276
|
+
|
|
277
|
+
self.current_scope=old_scope
|
|
278
|
+
|
|
279
|
+
return result
|
|
280
|
+
|
|
281
|
+
def visit_ReturnStatement(self, node):
|
|
282
|
+
value=None
|
|
283
|
+
|
|
284
|
+
if node.expr:
|
|
285
|
+
value=self.visit(node.expr)
|
|
286
|
+
|
|
287
|
+
raise ReturnSignal(value)
|
|
288
|
+
|
|
289
|
+
# ---------------- REMOVE LATER ----------------
|
|
290
|
+
|
|
291
|
+
def visit_PrintStatement(self, node):
|
|
292
|
+
value = self.visit(node.expr)
|
|
293
|
+
|
|
294
|
+
if value is None:
|
|
295
|
+
print('nulo')
|
|
296
|
+
elif value is True:
|
|
297
|
+
print('verdadeiro')
|
|
298
|
+
elif value is False:
|
|
299
|
+
print('falso')
|
|
300
|
+
else:
|
|
301
|
+
print(value)
|
|
File without changes
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
from lark import Transformer,v_args
|
|
2
|
+
|
|
3
|
+
from brasa.core.nodes.basics import *
|
|
4
|
+
from brasa.core.nodes.statements import *
|
|
5
|
+
from brasa.core.nodes.types import *
|
|
6
|
+
from brasa.core.nodes.values import *
|
|
7
|
+
from brasa.core.types.lvalues import *
|
|
8
|
+
|
|
9
|
+
from brasa.core.nodes.operators import BinaryOperation,UnaryOperation
|
|
10
|
+
from brasa.core.types.operators import BinaryOperationEnum,UnaryOperationEnum
|
|
11
|
+
|
|
12
|
+
class ASTBuilder(Transformer):
|
|
13
|
+
# ---------------- PROGRAM ----------------
|
|
14
|
+
|
|
15
|
+
def program(self,statements):
|
|
16
|
+
return Program(statements=statements)
|
|
17
|
+
|
|
18
|
+
@v_args(inline=True)
|
|
19
|
+
def statement(self, item):
|
|
20
|
+
return item
|
|
21
|
+
|
|
22
|
+
def block(self,statements):
|
|
23
|
+
return Block(list(statements))
|
|
24
|
+
|
|
25
|
+
# ---------------- VARIABLES ----------------
|
|
26
|
+
|
|
27
|
+
def ID(self,name):
|
|
28
|
+
return Identifier(name=str(name))
|
|
29
|
+
|
|
30
|
+
@v_args(inline=True)
|
|
31
|
+
def var_declaration(self,const,type_,id,expr):
|
|
32
|
+
return VariableDeclarationStatement(
|
|
33
|
+
id=id,
|
|
34
|
+
type=type_,
|
|
35
|
+
expr=expr,
|
|
36
|
+
is_const=const is not None
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@v_args(inline=True)
|
|
40
|
+
def var_declaration_null(self,const,type_,id):
|
|
41
|
+
return VariableDeclarationStatement(
|
|
42
|
+
id=id,
|
|
43
|
+
type=type_,
|
|
44
|
+
expr=NullValue(),
|
|
45
|
+
is_const=const is not None
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@v_args(inline=True)
|
|
49
|
+
def assigment(self,id,expr):
|
|
50
|
+
return AssignmentStatement(
|
|
51
|
+
target=id,
|
|
52
|
+
expr=expr
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@v_args(inline=True)
|
|
56
|
+
def compound_assignment(self,id,op,expr):
|
|
57
|
+
return CompoundAssignmentStatement(
|
|
58
|
+
target=id,
|
|
59
|
+
op=op,
|
|
60
|
+
expr=expr
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@v_args(inline=True)
|
|
64
|
+
def postfix(self,id,op):
|
|
65
|
+
return PostfixStatement(
|
|
66
|
+
target=id,
|
|
67
|
+
op=op
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def OP_POSTFIX(self, token):
|
|
71
|
+
return token.value
|
|
72
|
+
|
|
73
|
+
@v_args(inline=True)
|
|
74
|
+
def var_lvalue(self, id_):
|
|
75
|
+
return VarLValue(id_.name)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@v_args(inline=True)
|
|
79
|
+
def index_lvalue(self, base, index):
|
|
80
|
+
return IndexLValue(base, index)
|
|
81
|
+
|
|
82
|
+
# ---------------- TYPES ----------------
|
|
83
|
+
|
|
84
|
+
def int_type(self,_):
|
|
85
|
+
return IntegerType()
|
|
86
|
+
|
|
87
|
+
def float_type(self,_):
|
|
88
|
+
return FloatType()
|
|
89
|
+
|
|
90
|
+
@v_args(inline=True)
|
|
91
|
+
def nullable_type(self,type_):
|
|
92
|
+
return NullableType(base_type=type_)
|
|
93
|
+
|
|
94
|
+
def string_type(self,_):
|
|
95
|
+
return StringType()
|
|
96
|
+
|
|
97
|
+
def bool_type(self, _):
|
|
98
|
+
return BooleanType()
|
|
99
|
+
|
|
100
|
+
def void_type(self, _):
|
|
101
|
+
return VoidType()
|
|
102
|
+
|
|
103
|
+
@v_args(inline=True)
|
|
104
|
+
def array_type(self,element_type,size):
|
|
105
|
+
return ArrayType(
|
|
106
|
+
element_type=element_type,
|
|
107
|
+
size=size
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# ---------------- LITERALS ----------------
|
|
111
|
+
|
|
112
|
+
@v_args(inline=True)
|
|
113
|
+
def integer_literal(self,value):
|
|
114
|
+
return IntegerValue(value=int(value))
|
|
115
|
+
|
|
116
|
+
@v_args(inline=True)
|
|
117
|
+
def float_literal(self,value):
|
|
118
|
+
return FloatValue(value=float(value))
|
|
119
|
+
|
|
120
|
+
@v_args(inline=True)
|
|
121
|
+
def string_literal(self, token):
|
|
122
|
+
return StringLiteral(value=token[1:-1])
|
|
123
|
+
|
|
124
|
+
def null_literal(self,_):
|
|
125
|
+
return NullValue()
|
|
126
|
+
|
|
127
|
+
def true_literal(self, _):
|
|
128
|
+
return BooleanValue(True)
|
|
129
|
+
|
|
130
|
+
def false_literal(self, _):
|
|
131
|
+
return BooleanValue(False)
|
|
132
|
+
|
|
133
|
+
@v_args(inline=True)
|
|
134
|
+
def array_literal(self,*elements):
|
|
135
|
+
if len(elements)==1 and elements[0] is None:
|
|
136
|
+
elements=[]
|
|
137
|
+
|
|
138
|
+
return ArrayValue(elements=elements)
|
|
139
|
+
|
|
140
|
+
@v_args(inline=True)
|
|
141
|
+
def index_expr(self, base, index):
|
|
142
|
+
return IndexExpression(base, index)
|
|
143
|
+
|
|
144
|
+
# ---------------- CONTROL FLOW ----------------
|
|
145
|
+
|
|
146
|
+
@v_args(inline=True)
|
|
147
|
+
def if_statement(self, cond, then_block, else_branch=None):
|
|
148
|
+
return IfStatement(cond, then_block, else_branch)
|
|
149
|
+
|
|
150
|
+
@v_args(inline=True)
|
|
151
|
+
def while_statement(self, condition, body):
|
|
152
|
+
return WhileStatement(condition, body)
|
|
153
|
+
|
|
154
|
+
def break_statement(self, _):
|
|
155
|
+
return BreakStatement()
|
|
156
|
+
|
|
157
|
+
def continue_statement(self, _):
|
|
158
|
+
return ContinueStatement()
|
|
159
|
+
|
|
160
|
+
# ---------------- OPERATORS ----------------
|
|
161
|
+
|
|
162
|
+
# ---------------- MATH ----------------
|
|
163
|
+
|
|
164
|
+
@v_args(inline=True)
|
|
165
|
+
def add(self, left, right):
|
|
166
|
+
return BinaryOperation(left, BinaryOperationEnum.ADD, right)
|
|
167
|
+
|
|
168
|
+
@v_args(inline=True)
|
|
169
|
+
def sub(self, left, right):
|
|
170
|
+
return BinaryOperation(left, BinaryOperationEnum.SUB, right)
|
|
171
|
+
|
|
172
|
+
@v_args(inline=True)
|
|
173
|
+
def mul(self, left, right):
|
|
174
|
+
return BinaryOperation(left, BinaryOperationEnum.MUL, right)
|
|
175
|
+
|
|
176
|
+
@v_args(inline=True)
|
|
177
|
+
def div(self, left, right):
|
|
178
|
+
return BinaryOperation(left, BinaryOperationEnum.DIV, right)
|
|
179
|
+
|
|
180
|
+
@v_args(inline=True)
|
|
181
|
+
def neg(self, value):
|
|
182
|
+
return UnaryOperation(UnaryOperationEnum.NEG, value)
|
|
183
|
+
|
|
184
|
+
@v_args(inline=True)
|
|
185
|
+
def OP_ASSIGN(self, token):
|
|
186
|
+
return {
|
|
187
|
+
'+=': BinaryOperationEnum.ADD,
|
|
188
|
+
'-=': BinaryOperationEnum.SUB,
|
|
189
|
+
'*=': BinaryOperationEnum.MUL,
|
|
190
|
+
'/=': BinaryOperationEnum.DIV,
|
|
191
|
+
}[token]
|
|
192
|
+
|
|
193
|
+
@v_args(inline=True)
|
|
194
|
+
def OP_POSTFIX(self, token):
|
|
195
|
+
if token=='++':
|
|
196
|
+
return BinaryOperationEnum.ADD
|
|
197
|
+
|
|
198
|
+
return BinaryOperationEnum.SUB
|
|
199
|
+
|
|
200
|
+
# ---------------- COMPARISONS ----------------
|
|
201
|
+
|
|
202
|
+
@v_args(inline=True)
|
|
203
|
+
def gt(self, left, right):
|
|
204
|
+
return BinaryOperation(left, BinaryOperationEnum.GT, right)
|
|
205
|
+
|
|
206
|
+
@v_args(inline=True)
|
|
207
|
+
def lt(self, left, right):
|
|
208
|
+
return BinaryOperation(left, BinaryOperationEnum.LT, right)
|
|
209
|
+
|
|
210
|
+
@v_args(inline=True)
|
|
211
|
+
def ge(self, left, right):
|
|
212
|
+
return BinaryOperation(left, BinaryOperationEnum.GE, right)
|
|
213
|
+
|
|
214
|
+
@v_args(inline=True)
|
|
215
|
+
def le(self, left, right):
|
|
216
|
+
return BinaryOperation(left, BinaryOperationEnum.LE, right)
|
|
217
|
+
|
|
218
|
+
@v_args(inline=True)
|
|
219
|
+
def eq(self, left, right):
|
|
220
|
+
return BinaryOperation(left, BinaryOperationEnum.EQ, right)
|
|
221
|
+
|
|
222
|
+
@v_args(inline=True)
|
|
223
|
+
def ne(self, left, right):
|
|
224
|
+
return BinaryOperation(left, BinaryOperationEnum.NE, right)
|
|
225
|
+
|
|
226
|
+
# ---------------- LOGICAL ----------------
|
|
227
|
+
|
|
228
|
+
@v_args(inline=True)
|
|
229
|
+
def and_op(self, left, right):
|
|
230
|
+
return BinaryOperation(left, BinaryOperationEnum.AND, right)
|
|
231
|
+
|
|
232
|
+
@v_args(inline=True)
|
|
233
|
+
def or_op(self, left, right):
|
|
234
|
+
return BinaryOperation(left, BinaryOperationEnum.OR, right)
|
|
235
|
+
|
|
236
|
+
@v_args(inline=True)
|
|
237
|
+
def not_op(self, value):
|
|
238
|
+
return UnaryOperation(UnaryOperationEnum.NOT, value)
|
|
239
|
+
|
|
240
|
+
# ---------------- FUNCTION ----------------
|
|
241
|
+
|
|
242
|
+
@v_args(inline=True)
|
|
243
|
+
def func_declaration(self,func_name,parameters,return_type,block):
|
|
244
|
+
return FunctionDeclaration(
|
|
245
|
+
name=func_name,
|
|
246
|
+
params=[] if parameters is None else parameters,
|
|
247
|
+
return_type=VoidType() if return_type is None else return_type,
|
|
248
|
+
body=block
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
@v_args(inline=True)
|
|
252
|
+
def func_type(self, params=None, return_type=None):
|
|
253
|
+
if params is None:
|
|
254
|
+
params = []
|
|
255
|
+
return FunctionType(params, return_type)
|
|
256
|
+
|
|
257
|
+
@v_args(inline=True)
|
|
258
|
+
def call_func(self,id,parameters):
|
|
259
|
+
return CallExpression(
|
|
260
|
+
callee=id,
|
|
261
|
+
args=[] if parameters is None else parameters
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
@v_args(inline=True)
|
|
265
|
+
def return_type(self, type_):
|
|
266
|
+
return type_
|
|
267
|
+
|
|
268
|
+
def args_list(self, params):
|
|
269
|
+
return params
|
|
270
|
+
|
|
271
|
+
def param_list(self, params):
|
|
272
|
+
return params
|
|
273
|
+
|
|
274
|
+
@v_args(inline=True)
|
|
275
|
+
def param(self, type_, name):
|
|
276
|
+
return (type_, name)
|
|
277
|
+
|
|
278
|
+
@v_args(inline=True)
|
|
279
|
+
def return_statement(self, expr=None):
|
|
280
|
+
return ReturnStatement(expr)
|
|
281
|
+
|
|
282
|
+
@v_args(inline=True)
|
|
283
|
+
def lambda_expr(self, params=None, return_type=None, body=None):
|
|
284
|
+
if params is None:
|
|
285
|
+
params = []
|
|
286
|
+
|
|
287
|
+
if return_type is None:
|
|
288
|
+
return_type = VoidType()
|
|
289
|
+
|
|
290
|
+
return LambdaExpression(
|
|
291
|
+
params=params,
|
|
292
|
+
return_type=return_type,
|
|
293
|
+
body=body
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# ---------------- REMOVE LATER ----------------
|
|
297
|
+
|
|
298
|
+
@v_args(inline=True)
|
|
299
|
+
def print_variable(self,expr):
|
|
300
|
+
return PrintStatement(expr)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from brasa.runner import Interpreter
|
|
2
|
+
from brasa.runner import run_code
|
|
3
|
+
|
|
4
|
+
import readline
|
|
5
|
+
|
|
6
|
+
readline.read_history_file('.brasa_history')
|
|
7
|
+
readline.write_history_file('.brasa_history')
|
|
8
|
+
|
|
9
|
+
def repl():
|
|
10
|
+
interpreter=Interpreter()
|
|
11
|
+
buffer=''
|
|
12
|
+
|
|
13
|
+
print(r'''
|
|
14
|
+
==========================================================
|
|
15
|
+
_______ _______ __ ________ __
|
|
16
|
+
| _ "\ /" \ /""\ /" ) /""\
|
|
17
|
+
(. |_) :)|: | / \ (: \___/ / \
|
|
18
|
+
|: \/ |_____/ ) /' /\ \ \___ \ /' /\ \
|
|
19
|
+
(| _ \\ // / // __' \ __/ \\ // __' \
|
|
20
|
+
|: |_) :)|: __ \ / / \\ \ /" \ :)/ / \\ \
|
|
21
|
+
(_______/ |__| \___)(___/ \___)(_______/(___/ \___)
|
|
22
|
+
|
|
23
|
+
==========================================================
|
|
24
|
+
|
|
25
|
+
Type "exit" or "quit" to leave Brasa Interactive Environment
|
|
26
|
+
''')
|
|
27
|
+
|
|
28
|
+
while True:
|
|
29
|
+
try:
|
|
30
|
+
prompt='>>> ' if not buffer else '... '
|
|
31
|
+
line=input(prompt)
|
|
32
|
+
|
|
33
|
+
if line.strip() in {'exit','quit'}:
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
buffer+=line+'\n'
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
result=run_code(
|
|
40
|
+
buffer,
|
|
41
|
+
interpreter=interpreter
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
buffer=''
|
|
45
|
+
|
|
46
|
+
if result is not None:
|
|
47
|
+
print(result)
|
|
48
|
+
except Exception:
|
|
49
|
+
continue
|
|
50
|
+
except KeyboardInterrupt:
|
|
51
|
+
print('\nKeyboardInterrupt')
|
|
52
|
+
buffer=''
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from lark import Lark
|
|
4
|
+
|
|
5
|
+
from brasa.parser.ast_builder import ASTBuilder
|
|
6
|
+
from brasa.interpreter.interpreter import Interpreter
|
|
7
|
+
|
|
8
|
+
def load_grammar(grammar_path):
|
|
9
|
+
with open(
|
|
10
|
+
grammar_path,
|
|
11
|
+
'r',
|
|
12
|
+
encoding='utf-8'
|
|
13
|
+
) as g:
|
|
14
|
+
parser=Lark(
|
|
15
|
+
g.read(),
|
|
16
|
+
start='start',
|
|
17
|
+
maybe_placeholders=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
return parser
|
|
21
|
+
|
|
22
|
+
def run_code(
|
|
23
|
+
code:str,
|
|
24
|
+
interpreter=None
|
|
25
|
+
):
|
|
26
|
+
parser=load_grammar(
|
|
27
|
+
Path(__file__).parent/'parser'/'grammar.lark'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
raw_tree=parser.parse(code)
|
|
31
|
+
ast=ASTBuilder().transform(raw_tree)
|
|
32
|
+
|
|
33
|
+
if interpreter is None:
|
|
34
|
+
interpreter = Interpreter()
|
|
35
|
+
|
|
36
|
+
return interpreter.visit(ast)
|
|
37
|
+
|
|
38
|
+
def run_file(file_path:str):
|
|
39
|
+
file_path=Path(file_path).resolve()
|
|
40
|
+
|
|
41
|
+
if not file_path.exists():
|
|
42
|
+
print(f'Error: File "{file_path}" does not exist.')
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
with open(file_path,'r') as f:
|
|
46
|
+
code=f.read()
|
|
47
|
+
|
|
48
|
+
run_code(code)
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class Scope:
|
|
2
|
+
def __init__(self, parent=None):
|
|
3
|
+
self.parent = parent
|
|
4
|
+
self.symbols = {} # variable name -> entity id
|
|
5
|
+
|
|
6
|
+
# ---------------- DECLARE ----------------
|
|
7
|
+
def declare(self, name, var_id):
|
|
8
|
+
if name in self.symbols:
|
|
9
|
+
raise Exception(f'Error: Variable "{name}" has already been declared in this scope')
|
|
10
|
+
|
|
11
|
+
self.symbols[name] = var_id
|
|
12
|
+
|
|
13
|
+
# ---------------- LOOKUP ----------------
|
|
14
|
+
def lookup(self, name):
|
|
15
|
+
if name in self.symbols:
|
|
16
|
+
return self.symbols[name]
|
|
17
|
+
|
|
18
|
+
if self.parent:
|
|
19
|
+
return self.parent.lookup(name)
|
|
20
|
+
|
|
21
|
+
raise Exception(f'Variable "{name}" has not been found')
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class World:
|
|
2
|
+
def __init__(self):
|
|
3
|
+
self.next_id = 0
|
|
4
|
+
|
|
5
|
+
self.types = {} # entity id -> Type
|
|
6
|
+
self.values = {} # entity id -> Value
|
|
7
|
+
self.const = set() # (entity id)[]
|
|
8
|
+
|
|
9
|
+
# ---------------- CREATE VARIABLE ----------------
|
|
10
|
+
def create(self, type_, value, is_const=False):
|
|
11
|
+
var_id = self.next_id
|
|
12
|
+
self.next_id += 1
|
|
13
|
+
self.types[var_id] = type_
|
|
14
|
+
self.values[var_id] = value
|
|
15
|
+
|
|
16
|
+
if is_const:
|
|
17
|
+
self.const.add(var_id)
|
|
18
|
+
|
|
19
|
+
return var_id
|
|
20
|
+
|
|
21
|
+
# ---------------- GETTERS ----------------
|
|
22
|
+
def get_type(self, var_id):
|
|
23
|
+
return self.types[var_id]
|
|
24
|
+
|
|
25
|
+
def get_value(self, var_id):
|
|
26
|
+
return self.values[var_id]
|
|
27
|
+
|
|
28
|
+
def set_value(self, var_id, value):
|
|
29
|
+
if var_id in self.const:
|
|
30
|
+
raise Exception('Cannot modify const variable')
|
|
31
|
+
|
|
32
|
+
self.values[var_id] = value
|
|
33
|
+
|
|
34
|
+
def is_const(self, var_id):
|
|
35
|
+
return var_id in self.const
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
brasa/__init__.py
|
|
4
|
+
brasa/cli.py
|
|
5
|
+
brasa/repl.py
|
|
6
|
+
brasa/runner.py
|
|
7
|
+
brasa/core/__init__.py
|
|
8
|
+
brasa/core/nodes/__init__.py
|
|
9
|
+
brasa/core/nodes/basics.py
|
|
10
|
+
brasa/core/nodes/operators.py
|
|
11
|
+
brasa/core/nodes/statements.py
|
|
12
|
+
brasa/core/nodes/types.py
|
|
13
|
+
brasa/core/nodes/values.py
|
|
14
|
+
brasa/core/types/__init__.py
|
|
15
|
+
brasa/core/types/lvalues.py
|
|
16
|
+
brasa/core/types/operators.py
|
|
17
|
+
brasa/interpreter/__init__.py
|
|
18
|
+
brasa/interpreter/interpreter.py
|
|
19
|
+
brasa/interpreter/signals.py
|
|
20
|
+
brasa/parser/__init__.py
|
|
21
|
+
brasa/parser/ast_builder.py
|
|
22
|
+
brasa/runtime/__init__.py
|
|
23
|
+
brasa/runtime/scope.py
|
|
24
|
+
brasa/runtime/world.py
|
|
25
|
+
brasa_lang.egg-info/PKG-INFO
|
|
26
|
+
brasa_lang.egg-info/SOURCES.txt
|
|
27
|
+
brasa_lang.egg-info/dependency_links.txt
|
|
28
|
+
brasa_lang.egg-info/entry_points.txt
|
|
29
|
+
brasa_lang.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
brasa
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "brasa-lang"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Brasa programming language"
|
|
5
|
+
authors = [{name="Adriano"}]
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
|
|
8
|
+
[project.scripts]
|
|
9
|
+
brasa = "brasa.cli:main"
|
|
10
|
+
|
|
11
|
+
[tool.setuptools.packages.find]
|
|
12
|
+
include = ["brasa*"]
|