Fork of daniellemaywood.uk/gleam — Wasm codegen work
7.4 kB
236 lines
1use super::*;
2use crate::type_::{FieldMap, HasType};
3
4pub type TypedConstant = Constant<Arc<Type>, EcoString>;
5pub type UntypedConstant = Constant<(), ()>;
6
7// TODO: remove RecordTag paramter
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Constant<T, RecordTag> {
10 Int {
11 location: SrcSpan,
12 value: EcoString,
13 int_value: BigInt,
14 },
15
16 Float {
17 location: SrcSpan,
18 value: EcoString,
19 float_value: LiteralFloatValue,
20 },
21
22 String {
23 location: SrcSpan,
24 value: EcoString,
25 },
26
27 Tuple {
28 location: SrcSpan,
29 elements: Vec<Self>,
30 },
31
32 List {
33 location: SrcSpan,
34 elements: Vec<Self>,
35 type_: T,
36 },
37
38 Record {
39 location: SrcSpan,
40 module: Option<(EcoString, SrcSpan)>,
41 name: EcoString,
42 arguments: Vec<CallArg<Self>>,
43 tag: RecordTag,
44 type_: T,
45 field_map: Option<FieldMap>,
46 record_constructor: Option<Box<ValueConstructor>>,
47 },
48
49 BitArray {
50 location: SrcSpan,
51 segments: Vec<BitArraySegment<Self, T>>,
52 },
53
54 Var {
55 location: SrcSpan,
56 module: Option<(EcoString, SrcSpan)>,
57 name: EcoString,
58 constructor: Option<Box<ValueConstructor>>,
59 type_: T,
60 },
61
62 StringConcatenation {
63 location: SrcSpan,
64 left: Box<Self>,
65 right: Box<Self>,
66 },
67
68 /// A placeholder constant used to allow module analysis to continue
69 /// even when there are type errors. Should never end up in generated code.
70 Invalid {
71 location: SrcSpan,
72 type_: T,
73 },
74}
75
76impl TypedConstant {
77 pub fn type_(&self) -> Arc<Type> {
78 match self {
79 Constant::Int { .. } => type_::int(),
80 Constant::Float { .. } => type_::float(),
81 Constant::String { .. } | Constant::StringConcatenation { .. } => type_::string(),
82 Constant::BitArray { .. } => type_::bit_array(),
83 Constant::Tuple { elements, .. } => {
84 type_::tuple(elements.iter().map(|element| element.type_()).collect())
85 }
86 Constant::List { type_, .. }
87 | Constant::Record { type_, .. }
88 | Constant::Var { type_, .. }
89 | Constant::Invalid { type_, .. } => type_.clone(),
90 }
91 }
92
93 pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
94 if !self.location().contains(byte_index) {
95 return None;
96 }
97 Some(match self {
98 Constant::Int { .. }
99 | Constant::Float { .. }
100 | Constant::String { .. }
101 | Constant::Var { .. }
102 | Constant::Invalid { .. } => Located::Constant(self),
103 Constant::Tuple { elements, .. } | Constant::List { elements, .. } => elements
104 .iter()
105 .find_map(|element| element.find_node(byte_index))
106 .unwrap_or(Located::Constant(self)),
107 Constant::Record { arguments, .. } => arguments
108 .iter()
109 .find_map(|argument| argument.find_node(byte_index))
110 .unwrap_or(Located::Constant(self)),
111 Constant::BitArray { segments, .. } => segments
112 .iter()
113 .find_map(|segment| segment.find_node(byte_index))
114 .unwrap_or(Located::Constant(self)),
115 Constant::StringConcatenation { left, right, .. } => left
116 .find_node(byte_index)
117 .or_else(|| right.find_node(byte_index))
118 .unwrap_or(Located::Constant(self)),
119 })
120 }
121
122 pub fn definition_location(&self) -> Option<DefinitionLocation> {
123 match self {
124 Constant::Int { .. }
125 | Constant::Float { .. }
126 | Constant::String { .. }
127 | Constant::Tuple { .. }
128 | Constant::List { .. }
129 | Constant::BitArray { .. }
130 | Constant::StringConcatenation { .. }
131 | Constant::Invalid { .. } => None,
132 Constant::Record {
133 record_constructor: value_constructor,
134 ..
135 }
136 | Constant::Var {
137 constructor: value_constructor,
138 ..
139 } => value_constructor
140 .as_ref()
141 .map(|constructor| constructor.definition_location()),
142 }
143 }
144
145 pub(crate) fn referenced_variables(&self) -> im::HashSet<&EcoString> {
146 match self {
147 Constant::Var { name, .. } => im::hashset![name],
148
149 Constant::Invalid { .. }
150 | Constant::Int { .. }
151 | Constant::Float { .. }
152 | Constant::String { .. } => im::hashset![],
153
154 Constant::List { elements, .. } | Constant::Tuple { elements, .. } => elements
155 .iter()
156 .map(|element| element.referenced_variables())
157 .fold(im::hashset![], im::HashSet::union),
158
159 Constant::Record { arguments, .. } => arguments
160 .iter()
161 .map(|argument| argument.value.referenced_variables())
162 .fold(im::hashset![], im::HashSet::union),
163
164 Constant::BitArray { segments, .. } => segments
165 .iter()
166 .map(|segment| {
167 segment
168 .options
169 .iter()
170 .map(|option| option.referenced_variables())
171 .fold(segment.value.referenced_variables(), im::HashSet::union)
172 })
173 .fold(im::hashset![], im::HashSet::union),
174
175 Constant::StringConcatenation { left, right, .. } => left
176 .referenced_variables()
177 .union(right.referenced_variables()),
178 }
179 }
180}
181
182impl HasType for TypedConstant {
183 fn type_(&self) -> Arc<Type> {
184 self.type_()
185 }
186}
187
188impl<A, B> Constant<A, B> {
189 pub fn location(&self) -> SrcSpan {
190 match self {
191 Constant::Int { location, .. }
192 | Constant::List { location, .. }
193 | Constant::Float { location, .. }
194 | Constant::Tuple { location, .. }
195 | Constant::String { location, .. }
196 | Constant::Record { location, .. }
197 | Constant::BitArray { location, .. }
198 | Constant::Var { location, .. }
199 | Constant::Invalid { location, .. }
200 | Constant::StringConcatenation { location, .. } => *location,
201 }
202 }
203
204 #[must_use]
205 pub fn can_have_multiple_per_line(&self) -> bool {
206 match self {
207 Constant::Int { .. }
208 | Constant::Float { .. }
209 | Constant::String { .. }
210 | Constant::Var { .. } => true,
211
212 Constant::Tuple { .. }
213 | Constant::List { .. }
214 | Constant::Record { .. }
215 | Constant::BitArray { .. }
216 | Constant::StringConcatenation { .. }
217 | Constant::Invalid { .. } => false,
218 }
219 }
220}
221
222impl<A, B> HasLocation for Constant<A, B> {
223 fn location(&self) -> SrcSpan {
224 self.location()
225 }
226}
227
228impl<A, B> bit_array::GetLiteralValue for Constant<A, B> {
229 fn as_int_literal(&self) -> Option<BigInt> {
230 if let Constant::Int { int_value, .. } = self {
231 Some(int_value.clone())
232 } else {
233 None
234 }
235 }
236}