Skip to content

Commit 0c75097

Browse files
committed
type_checkers: call
1 parent 1825a42 commit 0c75097

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

src/type_checkers/call.cr

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# -----------------------------------------------------------------------
2+
# This file is part of MoonScript
3+
#
4+
# MoonSript is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# MoonSript is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with MoonSript. If not, see <https://www.gnu.org/licenses/>.
16+
#
17+
# Copyright (C) 2025 Krisna Pranav, MoonScript Developers
18+
# -----------------------------------------------------------------------
19+
20+
module MoonScript
21+
class TypeChecker
22+
def check(node : Ast::Call)
23+
function_type =
24+
resolve node.expression
25+
26+
check_call(node, function_type)
27+
end
28+
29+
def check_call(node, function_type) : Checkable
30+
return error! :call_not_a_function do
31+
snippet "The entity you called is not a function:", function_type
32+
snippet "The call in question:", node
33+
end unless function_type.name == "Function"
34+
35+
argument_size =
36+
function_type.parameters.size - 1
37+
38+
required_argument_size =
39+
case function_type
40+
when TypeChecker::Type
41+
argument_size - function_type.optional_count
42+
else
43+
argument_size
44+
end
45+
46+
parameters =
47+
[] of Checkable
48+
49+
error! :call_argument_size_mismatch do
50+
block do
51+
text "The function called"
52+
bold argument_size.to_s
53+
text "arguments, while you tried to call it with"
54+
bold "#{node.arguments.size}."
55+
end
56+
57+
snippet "The type of the function is:", function_type
58+
snippet "The call in question is here:", node
59+
end if node.arguments.size > argument_size ||
60+
node.arguments.size < required_argument_size
61+
62+
args =
63+
if node.arguments.all?(&.key.nil?)
64+
node.arguments
65+
elsif node.arguments.all?(&.key.!=(nil))
66+
node.arguments.sort_by do |argument|
67+
index =
68+
function_type
69+
.parameters
70+
.index { |param| param.label == argument.key.try(&.value) }
71+
72+
error! :call_not_found_argument do
73+
snippet(
74+
"looking for a named argument, can't find it:",
75+
argument.key.try(&.value).to_s)
76+
77+
snippet "The type of the function is:", function_type
78+
snippet "The call in question is here:", node
79+
end unless index
80+
81+
index
82+
end
83+
else
84+
error! :call_with_mixed_arguments do
85+
block "A call cannot have named and unamed arguments at the same " \
86+
"time because in specific cases I cannot pair the arguments " \
87+
"with the values."
88+
89+
snippet "The call in question is here:", node
90+
end
91+
end
92+
93+
args.each_with_index do |argument, index|
94+
argument_type =
95+
resolve argument
96+
97+
function_argument_type =
98+
function_type.parameters[index]
99+
100+
error! :call_argument_type_mismatch do
101+
ordinal =
102+
ordinal(index + 1)
103+
104+
block do
105+
text "The"
106+
bold "#{ordinal} argument"
107+
text "to a function is causing a mismatch."
108+
end
109+
110+
snippet "The function is expecting the #{ordinal} argument to be:", function_argument_type
111+
snippet "Instead it is:", argument_type
112+
snippet "The call in question is here:", node
113+
end unless res = Comparer.compare(function_argument_type, argument_type)
114+
115+
parameters << res
116+
end
117+
118+
if (optional_param_count = argument_size - args.size) > 0
119+
parameters.concat(function_type.parameters[args.size, optional_param_count])
120+
end
121+
122+
call_type =
123+
Type.new("Function", parameters + [function_type.parameters.last])
124+
125+
result =
126+
Comparer.compare(function_type, call_type)
127+
128+
error! :impossible_error do
129+
block "You have run into an impossible error. Please file an issue " \
130+
"with a reproducible example in the Github repository."
131+
132+
snippet "Call type:", call_type
133+
snippet "Function type:", function_type
134+
snippet node
135+
end unless result
136+
137+
final = resolve_type(result.parameters.last)
138+
139+
if node.await && final.name == "Promise"
140+
final.parameters.first
141+
else
142+
final
143+
end
144+
end
145+
end
146+
end

0 commit comments

Comments
 (0)