-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunction.py
More file actions
113 lines (92 loc) · 3.27 KB
/
function.py
File metadata and controls
113 lines (92 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import types
from error.displayError import displayError
from functionProps.argRanges import ArgRange
from nodes import Node, NodeResult
from errorTypes import ErrorType
from functionProps import FunctionResult, FunctionArg
import interpreter as inter
class Function:
def __init__(self, name : str, args : list, body : Node | types.FunctionType | types.BuiltinFunctionType, expression = "", description="") -> None:
"""
Function object to handle various functions in the language
Args:
name (str): name of the function
args (list<FunctionArg>): list of arguments names with their range
body (Node | types.FunctionType | types.BuiltinFunctionType): function to run when called
expression (str): literal expression of the function
"""
self.name = name
self.args = args
self.body = body
self.expression = expression
self.description = description
def check_args(self, args : list) -> bool:
"""
Checks if the arguments passed to the function are valid
Args:
args (list<NodeResult>): list of arguments passed to the function
Returns:
bool: True if the arguments are valid, False otherwise
"""
return len(args) == len(self.args)
def check_args_range(self, args : list) -> tuple:
"""
Checks if the arguments passed to the function are within their range
Args:
args (list<NodeResult>): list of arguments passed to the function
Returns:
tuple: (bool, list<NodeResult>): (True if the arguments are valid, list of arguments)
"""
for i in range(len(args)):
arg = args[i]
if self.args[i].arg_range:
in_range = self.args[i].check_arg_range(arg.value)
if not in_range:
return False, NodeResult(
None,
arg.pos,
ErrorType.DomainError,
f"Argument {i} of function {self.name} with value {arg.value} is out of {ArgRange.format_range(self.args[i].arg_range)}"
)
return True, None
def check_arg_range(self, arg_index : int, arg_value : int | float | bool) -> bool:
if self.args[arg_index].arg_range:
return self.args[arg_index].check_arg_range(arg_value)
else:
return True
def __call__(self, symbol_table : dict, args : list) -> NodeResult:
"""
Run the function with given arguments or interpret the function body
Args:
symbol_table (dict): parent symbol table
args (list<NodeResult>): arguments to pass to the function
Returns:
NodeResult: result or error of the function
"""
check = self.check_args_range(args)
if not check[0]:
return check[1]
if issubclass(type(self.body), Node):
symbol_table = symbol_table.copy()
for i in range(len(self.args)):
symbol_table[self.args[i].name] = args[i].value
i = inter.Interpreter(symbol_table)
result = i.visit_node(self.body)
if result.error:
displayError(
self.expression,
result.error,
result.pos,
result.message
)
return FunctionResult(None, result.error)
return FunctionResult(result.value)
args_value = list(map(lambda x: x.value, args))
if isinstance(self.body, types.BuiltinFunctionType):
return FunctionResult(self.body(*args_value))
else:
return self.body(*args_value, symbol_table)
def __str__(self) -> str:
return f"{self.name}({', '.join(map(lambda x : str(x), self.args))}) = {self.body}"
def __repr__(self) -> str:
return self.__str__()