Skip to content

Commit 034f718

Browse files
committed
type_checkers: componenet
1 parent 9510054 commit 034f718

File tree

1 file changed

+219
-0
lines changed

1 file changed

+219
-0
lines changed

src/type_checkers/component.cr

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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_all(node : Ast::Component) : Checkable
23+
resolve node
24+
25+
resolve node.constants
26+
resolve node.functions
27+
resolve node.gets
28+
29+
VOID
30+
end
31+
32+
def check(node : Ast::Component) : Checkable
33+
check_global_names node.name.value, node
34+
35+
checked =
36+
{} of String => Ast::Node
37+
38+
check_names(node.properties, "component", checked)
39+
check_names(node.functions, "component", checked)
40+
check_names(node.states, "component", checked)
41+
check_names(node.gets, "component", checked)
42+
43+
if node.name.value == "Main" && (property = node.properties.first?)
44+
error! :component_main_properties do
45+
block do
46+
text "The"
47+
bold "Main"
48+
text "component cannot have properties."
49+
end
50+
51+
snippet "A property is already defined here:", property
52+
snippet "The component in qa:", node
53+
end
54+
end
55+
56+
node.refs.reduce({} of String => Ast::Node) do |memo, (variable, ref)|
57+
name = variable.value
58+
other = memo[name]?
59+
60+
error! :component_reference_name_conflict do
61+
snippet "There are multiple references with the name:", name
62+
snippet "references:", variable
63+
snippet "references:", other
64+
end if other
65+
66+
memo[name] = variable
67+
memo
68+
end
69+
70+
node.styles.reduce({} of String => Ast::Node) do |memo, style|
71+
name = style.name.value
72+
other = memo[name]?
73+
74+
error! :component_style_name_conflict do
75+
snippet "There are multiple styles with the name:", name
76+
snippet "references:", style
77+
snippet "references:", other
78+
end if other
79+
80+
memo[name] = style
81+
memo
82+
end
83+
84+
node.connects.each do |connect|
85+
other = (node.connects - [connect]).find(&.store.value.==(connect.store.value))
86+
87+
return error! :component_multiple_stores do
88+
block do
89+
text "The component is connected to the store"
90+
bold %("#{connect.store.value}")
91+
text "multiple times."
92+
end
93+
94+
snippet "references:", other
95+
snippet "references:", connect
96+
end if other
97+
end
98+
99+
node.connects.each do |connect|
100+
connect.keys.each do |key|
101+
variable = key.target || key.name
102+
other = checked[variable.value]?
103+
104+
error! :component_exposed_name_conflict do
105+
block do
106+
text "You cannot expose"
107+
bold %("#{variable.value}")
108+
text "from the store because the name is already taken."
109+
end
110+
111+
snippet "The entity with the same name is here:", other
112+
snippet "the expose is here:", key
113+
end if other
114+
end
115+
end
116+
117+
node.connects.reduce({} of String => Ast::Node) do |memo, connect|
118+
connect.keys.each do |key|
119+
variable = key.target || key.name
120+
other = memo[variable.value]?
121+
122+
error! :component_multiple_exposed do
123+
block do
124+
text "The entity"
125+
bold %("#{variable.value}")
126+
text "from a store is exposed multiple times."
127+
end
128+
129+
snippet "It is exposed here:", other
130+
snippet "", key
131+
end if other
132+
133+
memo[variable.value] = key
134+
end
135+
memo
136+
end
137+
138+
node.uses.each do |use|
139+
other = (node.uses - [use]).find(&.provider.value.==(use.provider.value))
140+
141+
error! :component_multiple_providers do
142+
block do
143+
text "You are subcribing to the provider"
144+
bold %("#{other.provider.value}")
145+
text "in a component multiple times."
146+
end
147+
148+
snippet "A subscription is here:", other
149+
snippet "subscription:", use
150+
end if other
151+
end
152+
153+
resolve node.properties
154+
resolve node.connects
155+
resolve node.states
156+
resolve node.uses
157+
158+
error! :component_no_render_function do
159+
block do
160+
text "A component must have a"
161+
bold "render function"
162+
text "This component does not have one:"
163+
end
164+
165+
snippet node
166+
end unless node.functions.any?(&.name.value.==("render"))
167+
168+
node.functions.each do |function|
169+
case function.name.value
170+
when "render"
171+
type =
172+
resolve function
173+
174+
matches =
175+
[HTML, STRING, HTML_CHILDREN, TEXT_CHILDREN].any? do |item|
176+
Comparer.compare(type, Type.new("Function", [item] of Checkable))
177+
end
178+
179+
error! :component_render_function_mismatch do
180+
block do
181+
text "expecting the type of the"
182+
bold "render"
183+
text "function to match one of types:"
184+
end
185+
186+
snippet <<-PLAIN
187+
Function(Array(String))
188+
Function(Array(Html))
189+
Function(String)
190+
Function(Html)
191+
PLAIN
192+
193+
snippet "Instead the type of the function is:", type.parameters.first
194+
snippet "function in question:", function
195+
end unless matches
196+
when "componentDidMount",
197+
"componentDidUpdate",
198+
"componentWillUnmount"
199+
type =
200+
resolve function
201+
202+
error! :component_lifecycle_function_mismatch do
203+
block do
204+
text "The type of the function"
205+
bold %("#{function.name.value}")
206+
text "of a component must be:"
207+
end
208+
209+
snippet VOID_FUNCTION
210+
snippet "Instead it is:", type
211+
snippet "function in question:", function
212+
end unless Comparer.compare(type, VOID_FUNCTION)
213+
end
214+
end
215+
216+
VOID
217+
end
218+
end
219+
end

0 commit comments

Comments
 (0)