Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / erlang / pattern.rs
9.3 kB 284 lines
1use std::cell::RefCell; 2 3use ecow::eco_format; 4 5use crate::analyse::Inferred; 6 7use super::*; 8 9pub(super) fn pattern<'a>( 10 p: &'a TypedPattern, 11 env: &mut Env<'a>, 12 guards: &mut Vec<Document<'a>>, 13) -> Document<'a> { 14 let mut vars = vec![]; 15 to_doc(p, &mut vars, env, guards) 16} 17 18fn print<'a>( 19 p: &'a TypedPattern, 20 vars: &mut Vec<&'a str>, 21 env: &mut Env<'a>, 22 guards: &mut Vec<Document<'a>>, 23) -> Document<'a> { 24 match p { 25 Pattern::Assign { 26 name, pattern: p, .. 27 } => { 28 vars.push(name); 29 print(p, vars, env, guards) 30 .append(" = ") 31 .append(env.next_local_var_name(name)) 32 } 33 34 Pattern::List { elements, tail, .. } => { 35 pattern_list(elements, tail.as_deref(), vars, env, guards) 36 } 37 38 Pattern::Discard { .. } => "_".to_doc(), 39 40 Pattern::BitArraySize(size) => bit_array_size(size, env), 41 42 Pattern::Variable { name, .. } => { 43 vars.push(name); 44 env.next_local_var_name(name) 45 } 46 47 Pattern::Int { value, .. } => int(value), 48 49 Pattern::Float { value, .. } => float(value), 50 51 Pattern::String { value, .. } => string(value), 52 53 Pattern::Constructor { 54 arguments: args, 55 constructor: Inferred::Known(PatternConstructor { name, .. }), 56 .. 57 } => tag_tuple_pattern(name, args, vars, env, guards), 58 59 Pattern::Constructor { 60 constructor: Inferred::Unknown, 61 .. 62 } => { 63 panic!("Erlang generation performed with uninferred pattern constructor") 64 } 65 66 Pattern::Tuple { elements, .. } => { 67 tuple(elements.iter().map(|p| print(p, vars, env, guards))) 68 } 69 70 Pattern::BitArray { segments, .. } => bit_array( 71 segments 72 .iter() 73 .map(|s| pattern_segment(&s.value, &s.options, vars, env, guards)), 74 ), 75 76 Pattern::StringPrefix { 77 left_side_string, 78 right_side_assignment, 79 left_side_assignment, 80 .. 81 } => { 82 let right = match right_side_assignment { 83 AssignName::Variable(right) => { 84 vars.push(right); 85 env.next_local_var_name(right) 86 } 87 AssignName::Discard(_) => "_".to_doc(), 88 }; 89 90 match left_side_assignment { 91 Some((left_name, _)) => { 92 // "wibble" as prefix <> rest 93 // ^^^^^^^^^ In case the left prefix of the pattern matching is given an alias 94 // we bind it to a local variable so that it can be correctly 95 // referenced inside the case branch. 96 // 97 // <<Prefix:3/binary, Rest/binary>> when Prefix =:= <<"wibble">> 98 // ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 99 // since erlang's binary pattern matching doesn't allow direct string assignment 100 // to variables within the pattern, we first match the expected prefix length in 101 // bytes, then use a guard clause to verify the content. 102 // 103 vars.push(left_name); 104 let name = env.next_local_var_name(left_name); 105 guards.push(docvec![name.clone(), " =:= ", string(left_side_string)]); 106 docvec![ 107 "<<", 108 name.clone(), 109 ":", 110 string_length_utf8_bytes(left_side_string), 111 "/binary", 112 ", ", 113 right, 114 "/binary>>", 115 ] 116 } 117 None => docvec![ 118 "<<\"", 119 string_inner(left_side_string), 120 "\"/utf8", 121 ", ", 122 right, 123 "/binary>>" 124 ], 125 } 126 } 127 128 Pattern::Invalid { .. } => panic!("invalid patterns should not reach code generation"), 129 } 130} 131 132fn bit_array_size<'a>(size: &'a TypedBitArraySize, env: &mut Env<'a>) -> Document<'a> { 133 match size { 134 BitArraySize::Int { value, .. } => int(value), 135 136 BitArraySize::Variable { 137 name, constructor, .. 138 } => { 139 let v = &constructor 140 .as_ref() 141 .expect("Constructor not found for variable usage") 142 .variant; 143 match v { 144 ValueConstructorVariant::ModuleConstant { literal, .. } => { 145 const_inline(literal, env) 146 } 147 _ => env.local_var_name(name), 148 } 149 } 150 } 151} 152 153pub(super) fn to_doc<'a>( 154 p: &'a TypedPattern, 155 vars: &mut Vec<&'a str>, 156 env: &mut Env<'a>, 157 guards: &mut Vec<Document<'a>>, 158) -> Document<'a> { 159 print(p, vars, env, guards) 160} 161 162fn tag_tuple_pattern<'a>( 163 name: &'a str, 164 args: &'a [CallArg<TypedPattern>], 165 vars: &mut Vec<&'a str>, 166 env: &mut Env<'a>, 167 guards: &mut Vec<Document<'a>>, 168) -> Document<'a> { 169 if args.is_empty() { 170 atom_string(to_snake_case(name)) 171 } else { 172 tuple( 173 [atom_string(to_snake_case(name))] 174 .into_iter() 175 .chain(args.iter().map(|p| print(&p.value, vars, env, guards))), 176 ) 177 } 178} 179 180fn pattern_segment<'a>( 181 value: &'a TypedPattern, 182 options: &'a [BitArrayOption<TypedPattern>], 183 vars: &mut Vec<&'a str>, 184 env: &mut Env<'a>, 185 guards: &mut Vec<Document<'a>>, 186) -> Document<'a> { 187 let pattern_is_a_string_literal = matches!(value, Pattern::String { .. }); 188 let pattern_is_a_discard = matches!(value, Pattern::Discard { .. }); 189 190 let vars = RefCell::new(vars); 191 let guards = RefCell::new(guards); 192 193 let create_document = |env: &mut Env<'a>| match value { 194 Pattern::String { value, .. } => value.to_doc().surround("\"", "\""), 195 Pattern::Discard { .. } 196 | Pattern::Variable { .. } 197 | Pattern::Int { .. } 198 | Pattern::Float { .. } => { 199 print(value, &mut vars.borrow_mut(), env, &mut guards.borrow_mut()) 200 } 201 202 Pattern::Assign { name, pattern, .. } => { 203 vars.borrow_mut().push(name); 204 let variable_name = env.next_local_var_name(name); 205 206 match pattern.as_ref() { 207 // In Erlang, assignment patterns inside bit arrays are not allowed. So instead of 208 // generating `<<1 = A>>`, we use guards, and generate `<<A>> when A =:= 1`. 209 Pattern::Int { value, .. } => { 210 guards 211 .borrow_mut() 212 .push(docvec![variable_name.clone(), " =:= ", int(value)]); 213 variable_name 214 } 215 Pattern::Float { value, .. } => { 216 guards 217 .borrow_mut() 218 .push(docvec![variable_name.clone(), " =:= ", float(value)]); 219 variable_name 220 } 221 222 // Here we do the same as for floats and ints, but we must calculate the size of 223 // the string first, so we can correctly match the bit array segment then compare 224 // it afterwards. 225 Pattern::String { value, .. } => { 226 guards.borrow_mut().push(docvec![ 227 variable_name.clone(), 228 " =:= ", 229 string(value) 230 ]); 231 docvec![variable_name, ":", string_length_utf8_bytes(value)] 232 } 233 234 // Doing a pattern such as `<<_ as a>>` is the same as just `<<a>>`, so we treat it 235 // as such. 236 Pattern::Discard { .. } => variable_name, 237 238 // Any other pattern is invalid as a bit array segment. We already handle the case 239 // of `<<a as b>>` in the type-checker, and assignment patterns cannot be nested. 240 _ => panic!("Pattern segment match not recognised"), 241 } 242 } 243 244 _ => panic!("Pattern segment match not recognised"), 245 }; 246 247 let size = |value: &'a TypedPattern, env: &mut Env<'a>| { 248 Some(":".to_doc().append(print( 249 value, 250 &mut vars.borrow_mut(), 251 env, 252 &mut guards.borrow_mut(), 253 ))) 254 }; 255 256 let unit = |value: &'a u8| Some(eco_format!("unit:{value}").to_doc()); 257 258 bit_array_segment( 259 create_document, 260 options, 261 size, 262 unit, 263 pattern_is_a_string_literal, 264 pattern_is_a_discard, 265 env, 266 ) 267} 268 269fn pattern_list<'a>( 270 elements: &'a [TypedPattern], 271 tail: Option<&'a TypedPattern>, 272 vars: &mut Vec<&'a str>, 273 env: &mut Env<'a>, 274 guards: &mut Vec<Document<'a>>, 275) -> Document<'a> { 276 let elements = join( 277 elements 278 .iter() 279 .map(|element| print(element, vars, env, guards)), 280 break_(",", ", "), 281 ); 282 let tail = tail.map(|tail| print(tail, vars, env, guards)); 283 list(elements, tail) 284}