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