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 / bit_array.rs
12 kB 418 lines
1use ecow::EcoString; 2use num_bigint::BigInt; 3 4use crate::ast::{self, BitArrayOption, SrcSpan}; 5use crate::build::Target; 6use crate::type_::Type; 7use std::sync::Arc; 8 9// 10// Public Interface 11// 12 13pub fn type_options_for_value<TypedValue>( 14 input_options: &[BitArrayOption<TypedValue>], 15 target: Target, 16) -> Result<Arc<Type>, Error> 17where 18 TypedValue: GetLiteralValue, 19{ 20 type_options(input_options, TypeOptionsMode::Expression, false, target) 21} 22 23pub fn type_options_for_pattern<TypedValue>( 24 input_options: &[BitArrayOption<TypedValue>], 25 must_have_size: bool, 26 target: Target, 27) -> Result<Arc<Type>, Error> 28where 29 TypedValue: GetLiteralValue, 30{ 31 type_options( 32 input_options, 33 TypeOptionsMode::Pattern, 34 must_have_size, 35 target, 36 ) 37} 38 39struct SegmentOptionCategories<'a, T> { 40 type_: Option<&'a BitArrayOption<T>>, 41 signed: Option<&'a BitArrayOption<T>>, 42 endian: Option<&'a BitArrayOption<T>>, 43 unit: Option<&'a BitArrayOption<T>>, 44 size: Option<&'a BitArrayOption<T>>, 45} 46 47impl<T> SegmentOptionCategories<'_, T> { 48 fn new() -> Self { 49 SegmentOptionCategories { 50 type_: None, 51 signed: None, 52 endian: None, 53 unit: None, 54 size: None, 55 } 56 } 57 58 fn segment_type(&self) -> Arc<Type> { 59 use BitArrayOption::*; 60 let default = Int { 61 location: SrcSpan::default(), 62 }; 63 64 match self.type_.unwrap_or(&default) { 65 Int { .. } => crate::type_::int(), 66 Float { .. } => crate::type_::float(), 67 Utf8 { .. } | Utf16 { .. } | Utf32 { .. } => crate::type_::string(), 68 Bytes { .. } | Bits { .. } => crate::type_::bit_array(), 69 Utf8Codepoint { .. } | Utf16Codepoint { .. } | Utf32Codepoint { .. } => { 70 crate::type_::utf_codepoint() 71 } 72 73 Signed { .. } 74 | Unsigned { .. } 75 | Big { .. } 76 | Little { .. } 77 | Native { .. } 78 | Size { .. } 79 | Unit { .. } => panic!("Tried to type a non type kind BitArray option."), 80 } 81 } 82} 83 84#[derive(Debug, PartialEq, Eq)] 85/// Whether we're typing options for a bit array segment that's part of a pattern 86/// or an expression. 87/// 88enum TypeOptionsMode { 89 Expression, 90 Pattern, 91} 92 93fn type_options<TypedValue>( 94 input_options: &[BitArrayOption<TypedValue>], 95 mode: TypeOptionsMode, 96 must_have_size: bool, 97 target: Target, 98) -> Result<Arc<Type>, Error> 99where 100 TypedValue: GetLiteralValue, 101{ 102 use BitArrayOption::*; 103 104 let mut categories = SegmentOptionCategories::new(); 105 // Basic category checking 106 for option in input_options { 107 match option { 108 Utf8Codepoint { .. } | Utf16Codepoint { .. } | Utf32Codepoint { .. } 109 if mode == TypeOptionsMode::Pattern && target == Target::JavaScript => 110 { 111 return err( 112 ErrorType::OptionNotSupportedForTarget { 113 target, 114 option: UnsupportedOption::UtfCodepointPattern, 115 }, 116 option.location(), 117 ); 118 } 119 120 Bytes { .. } 121 | Int { .. } 122 | Float { .. } 123 | Bits { .. } 124 | Utf8 { .. } 125 | Utf16 { .. } 126 | Utf32 { .. } 127 | Utf8Codepoint { .. } 128 | Utf16Codepoint { .. } 129 | Utf32Codepoint { .. } => { 130 if let Some(previous) = categories.type_ { 131 return err( 132 ErrorType::ConflictingTypeOptions { 133 existing_type: previous.label(), 134 }, 135 option.location(), 136 ); 137 } else { 138 categories.type_ = Some(option); 139 } 140 } 141 142 Signed { .. } | Unsigned { .. } => { 143 if let Some(previous) = categories.signed { 144 return err( 145 ErrorType::ConflictingSignednessOptions { 146 existing_signed: previous.label(), 147 }, 148 option.location(), 149 ); 150 } else { 151 categories.signed = Some(option); 152 } 153 } 154 155 Native { .. } if target == Target::JavaScript => { 156 return err( 157 ErrorType::OptionNotSupportedForTarget { 158 target, 159 option: UnsupportedOption::NativeEndianness, 160 }, 161 option.location(), 162 ); 163 } 164 165 Big { .. } | Little { .. } | Native { .. } => { 166 if let Some(previous) = categories.endian { 167 return err( 168 ErrorType::ConflictingEndiannessOptions { 169 existing_endianness: previous.label(), 170 }, 171 option.location(), 172 ); 173 } else { 174 categories.endian = Some(option); 175 } 176 } 177 178 Size { .. } => { 179 if categories.size.is_some() { 180 return err(ErrorType::ConflictingSizeOptions, option.location()); 181 } else { 182 categories.size = Some(option); 183 } 184 } 185 186 Unit { .. } => { 187 if categories.unit.is_some() { 188 return err(ErrorType::ConflictingUnitOptions, option.location()); 189 } else { 190 categories.unit = Some(option); 191 } 192 } 193 }; 194 } 195 196 // Some options are not allowed in value mode 197 if mode == TypeOptionsMode::Expression { 198 match categories { 199 SegmentOptionCategories { 200 signed: Some(opt), .. 201 } 202 | SegmentOptionCategories { 203 type_: Some(opt @ Bytes { .. }), 204 .. 205 } => return err(ErrorType::OptionNotAllowedInValue, opt.location()), 206 _ => (), 207 } 208 } 209 210 // All but the last segment in a pattern must have an exact size 211 if must_have_size 212 && let SegmentOptionCategories { 213 type_: Some(opt @ (Bytes { .. } | Bits { .. })), 214 size: None, 215 .. 216 } = categories 217 { 218 return err(ErrorType::SegmentMustHaveSize, opt.location()); 219 } 220 221 // Endianness is only valid for int, utf16, utf16_codepoint, utf32, 222 // utf32_codepoint and float 223 match categories { 224 SegmentOptionCategories { 225 type_: 226 None 227 | Some( 228 Int { .. } 229 | Utf16 { .. } 230 | Utf32 { .. } 231 | Utf16Codepoint { .. } 232 | Utf32Codepoint { .. } 233 | Float { .. }, 234 ), 235 .. 236 } => {} 237 238 SegmentOptionCategories { 239 endian: Some(endian), 240 .. 241 } => return err(ErrorType::InvalidEndianness, endian.location()), 242 243 _ => {} 244 } 245 246 // signed and unsigned can only be used with int types 247 match categories { 248 SegmentOptionCategories { 249 type_: None | Some(Int { .. }), 250 .. 251 } => {} 252 253 SegmentOptionCategories { 254 type_: Some(opt), 255 signed: Some(sign), 256 .. 257 } => { 258 return err( 259 ErrorType::SignednessUsedOnNonInt { type_: opt.label() }, 260 sign.location(), 261 ); 262 } 263 264 _ => {} 265 } 266 267 // utf8, utf16, utf32 exclude unit and size 268 match categories { 269 SegmentOptionCategories { 270 type_: Some(type_), 271 unit: Some(_), 272 .. 273 } if is_unicode(type_) => { 274 return err( 275 ErrorType::TypeDoesNotAllowUnit { 276 type_: type_.label(), 277 }, 278 type_.location(), 279 ); 280 } 281 282 SegmentOptionCategories { 283 type_: Some(type_), 284 size: Some(_), 285 .. 286 } if is_unicode(type_) => { 287 return err( 288 ErrorType::TypeDoesNotAllowSize { 289 type_: type_.label(), 290 }, 291 type_.location(), 292 ); 293 } 294 295 _ => {} 296 } 297 298 // if unit specified, size must be specified 299 if let SegmentOptionCategories { 300 unit: Some(unit), 301 size: None, 302 .. 303 } = categories 304 { 305 return err(ErrorType::UnitMustHaveSize, unit.location()); 306 } 307 308 // float only 16/32/64 309 if let SegmentOptionCategories { 310 type_: Some(Float { .. }), 311 size: Some(size), 312 .. 313 } = categories 314 && let Some(abox) = size.value() 315 { 316 match abox.as_int_literal() { 317 None => (), 318 Some(value) if value == 16.into() || value == 32.into() || value == 64.into() => (), 319 _ => return err(ErrorType::FloatWithSize, size.location()), 320 } 321 } 322 323 // Segment patterns with a zero or negative constant size must be rejected, 324 // we know they will never match! 325 // A negative size is still allowed in expressions as it will just result 326 // in an empty segment. 327 if let (Some(size @ Size { value, .. }), TypeOptionsMode::Pattern) = (categories.size, mode) { 328 match value.as_int_literal() { 329 Some(n) if n <= BigInt::ZERO => { 330 return err(ErrorType::ConstantSizeNotPositive, size.location()); 331 } 332 Some(_) | None => (), 333 } 334 } 335 336 Ok(categories.segment_type()) 337} 338 339pub trait GetLiteralValue { 340 fn as_int_literal(&self) -> Option<BigInt>; 341} 342 343impl GetLiteralValue for ast::TypedPattern { 344 fn as_int_literal(&self) -> Option<BigInt> { 345 match self { 346 ast::Pattern::Int { int_value, .. } 347 | ast::Pattern::BitArraySize(ast::BitArraySize::Int { int_value, .. }) => { 348 Some(int_value.clone()) 349 } 350 _ => None, 351 } 352 } 353} 354 355fn is_unicode<T>(opt: &BitArrayOption<T>) -> bool { 356 use BitArrayOption::*; 357 358 matches!( 359 opt, 360 Utf8 { .. } 361 | Utf16 { .. } 362 | Utf32 { .. } 363 | Utf8Codepoint { .. } 364 | Utf16Codepoint { .. } 365 | Utf32Codepoint { .. } 366 ) 367} 368 369fn err<A>(error: ErrorType, location: SrcSpan) -> Result<A, Error> { 370 Err(Error { location, error }) 371} 372 373#[derive(Debug)] 374pub struct Error { 375 pub location: SrcSpan, 376 pub error: ErrorType, 377} 378 379#[derive(Debug, PartialEq, Eq, Clone)] 380pub enum ErrorType { 381 ConflictingEndiannessOptions { 382 existing_endianness: EcoString, 383 }, 384 ConflictingSignednessOptions { 385 existing_signed: EcoString, 386 }, 387 ConflictingSizeOptions, 388 ConflictingTypeOptions { 389 existing_type: EcoString, 390 }, 391 ConflictingUnitOptions, 392 FloatWithSize, 393 InvalidEndianness, 394 OptionNotAllowedInValue, 395 SegmentMustHaveSize, 396 SignednessUsedOnNonInt { 397 type_: EcoString, 398 }, 399 TypeDoesNotAllowSize { 400 type_: EcoString, 401 }, 402 TypeDoesNotAllowUnit { 403 type_: EcoString, 404 }, 405 UnitMustHaveSize, 406 VariableUtfSegmentInPattern, 407 ConstantSizeNotPositive, 408 OptionNotSupportedForTarget { 409 target: Target, 410 option: UnsupportedOption, 411 }, 412} 413 414#[derive(Debug, PartialEq, Eq, Clone, Copy)] 415pub enum UnsupportedOption { 416 UtfCodepointPattern, 417 NativeEndianness, 418}