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
9.3 kB 334 lines
1use ecow::EcoString; 2 3use crate::ast::{BitArrayOption, SrcSpan}; 4use crate::type_::Type; 5use std::sync::Arc; 6 7// 8// Public Interface 9// 10 11pub fn type_options_for_value<TypedValue>( 12 input_options: &[BitArrayOption<TypedValue>], 13) -> Result<Arc<Type>, Error> 14where 15 TypedValue: GetLiteralValue, 16{ 17 type_options(input_options, true, false) 18} 19 20pub fn type_options_for_pattern<TypedValue>( 21 input_options: &[BitArrayOption<TypedValue>], 22 must_have_size: bool, 23) -> Result<Arc<Type>, Error> 24where 25 TypedValue: GetLiteralValue, 26{ 27 type_options(input_options, false, must_have_size) 28} 29 30struct SegmentOptionCategories<'a, T> { 31 typ: Option<&'a BitArrayOption<T>>, 32 signed: Option<&'a BitArrayOption<T>>, 33 endian: Option<&'a BitArrayOption<T>>, 34 unit: Option<&'a BitArrayOption<T>>, 35 size: Option<&'a BitArrayOption<T>>, 36} 37 38impl<T> SegmentOptionCategories<'_, T> { 39 fn new() -> Self { 40 SegmentOptionCategories { 41 typ: None, 42 signed: None, 43 endian: None, 44 unit: None, 45 size: None, 46 } 47 } 48 49 fn segment_type(&self) -> Arc<Type> { 50 use BitArrayOption::*; 51 let default = Int { 52 location: SrcSpan::default(), 53 }; 54 55 match self.typ.unwrap_or(&default) { 56 Int { .. } => crate::type_::int(), 57 Float { .. } => crate::type_::float(), 58 Utf8 { .. } | Utf16 { .. } | Utf32 { .. } => crate::type_::string(), 59 Bytes { .. } | Bits { .. } => crate::type_::bits(), 60 Utf8Codepoint { .. } | Utf16Codepoint { .. } | Utf32Codepoint { .. } => { 61 crate::type_::utf_codepoint() 62 } 63 64 Signed { .. } 65 | Unsigned { .. } 66 | Big { .. } 67 | Little { .. } 68 | Native { .. } 69 | Size { .. } 70 | Unit { .. } => panic!("Tried to type a non type kind BitArray option."), 71 } 72 } 73} 74 75fn type_options<TypedValue>( 76 input_options: &[BitArrayOption<TypedValue>], 77 value_mode: bool, 78 must_have_size: bool, 79) -> Result<Arc<Type>, Error> 80where 81 TypedValue: GetLiteralValue, 82{ 83 use BitArrayOption::*; 84 85 let mut categories = SegmentOptionCategories::new(); 86 // Basic category checking 87 for option in input_options { 88 match option { 89 Bytes { .. } 90 | Int { .. } 91 | Float { .. } 92 | Bits { .. } 93 | Utf8 { .. } 94 | Utf16 { .. } 95 | Utf32 { .. } 96 | Utf8Codepoint { .. } 97 | Utf16Codepoint { .. } 98 | Utf32Codepoint { .. } => { 99 if let Some(previous) = categories.typ { 100 return err( 101 ErrorType::ConflictingTypeOptions { 102 existing_type: previous.label(), 103 }, 104 option.location(), 105 ); 106 } else { 107 categories.typ = Some(option); 108 } 109 } 110 111 Signed { .. } | Unsigned { .. } => { 112 if let Some(previous) = categories.signed { 113 return err( 114 ErrorType::ConflictingSignednessOptions { 115 existing_signed: previous.label(), 116 }, 117 option.location(), 118 ); 119 } else { 120 categories.signed = Some(option); 121 } 122 } 123 124 Big { .. } | Little { .. } | Native { .. } => { 125 if let Some(previous) = categories.endian { 126 return err( 127 ErrorType::ConflictingEndiannessOptions { 128 existing_endianness: previous.label(), 129 }, 130 option.location(), 131 ); 132 } else { 133 categories.endian = Some(option); 134 } 135 } 136 137 Size { .. } => { 138 if categories.size.is_some() { 139 return err(ErrorType::ConflictingSizeOptions, option.location()); 140 } else { 141 categories.size = Some(option); 142 } 143 } 144 145 Unit { .. } => { 146 if categories.unit.is_some() { 147 return err(ErrorType::ConflictingUnitOptions, option.location()); 148 } else { 149 categories.unit = Some(option); 150 } 151 } 152 }; 153 } 154 155 // Some options are not allowed in value mode 156 if value_mode { 157 match categories { 158 SegmentOptionCategories { 159 signed: Some(opt), .. 160 } 161 | SegmentOptionCategories { 162 typ: Some(opt @ Bytes { .. }), 163 .. 164 } => return err(ErrorType::OptionNotAllowedInValue, opt.location()), 165 _ => (), 166 } 167 } 168 169 // All but the last segment in a pattern must have an exact size 170 if must_have_size { 171 if let SegmentOptionCategories { 172 typ: Some(opt @ (Bytes { .. } | Bits { .. })), 173 size: None, 174 .. 175 } = categories 176 { 177 return err(ErrorType::SegmentMustHaveSize, opt.location()); 178 } 179 } 180 181 // Endianness is only valid for int, utf6, utf32 and float 182 match categories { 183 SegmentOptionCategories { 184 typ: None | Some(Int { .. } | Utf16 { .. } | Utf32 { .. } | Float { .. }), 185 .. 186 } => {} 187 188 SegmentOptionCategories { 189 endian: Some(endian), 190 .. 191 } => return err(ErrorType::InvalidEndianness, endian.location()), 192 193 _ => {} 194 } 195 196 // signed and unsigned can only be used with int types 197 match categories { 198 SegmentOptionCategories { 199 typ: None | Some(Int { .. }), 200 .. 201 } => {} 202 203 SegmentOptionCategories { 204 typ: Some(opt), 205 signed: Some(sign), 206 .. 207 } => { 208 return err( 209 ErrorType::SignednessUsedOnNonInt { typ: opt.label() }, 210 sign.location(), 211 ); 212 } 213 214 _ => {} 215 } 216 217 // utf8, utf16, utf32 exclude unit and size 218 match categories { 219 SegmentOptionCategories { 220 typ: Some(typ), 221 unit: Some(_), 222 .. 223 } if is_unicode(typ) => { 224 return err( 225 ErrorType::TypeDoesNotAllowUnit { typ: typ.label() }, 226 typ.location(), 227 ); 228 } 229 230 SegmentOptionCategories { 231 typ: Some(typ), 232 size: Some(_), 233 .. 234 } if is_unicode(typ) => { 235 return err( 236 ErrorType::TypeDoesNotAllowSize { typ: typ.label() }, 237 typ.location(), 238 ); 239 } 240 241 _ => {} 242 } 243 244 // if unit specified, size must be specified 245 if let SegmentOptionCategories { 246 unit: Some(unit), 247 size: None, 248 .. 249 } = categories 250 { 251 return err(ErrorType::UnitMustHaveSize, unit.location()); 252 } 253 254 // float only 16/32/64 255 if let SegmentOptionCategories { 256 typ: Some(Float { .. }), 257 size: Some(size), 258 .. 259 } = categories 260 { 261 if let Some(abox) = size.value() { 262 match abox.as_int_literal() { 263 None => (), 264 Some(16) => (), 265 Some(32) => (), 266 Some(64) => (), 267 _ => return err(ErrorType::FloatWithSize, size.location()), 268 } 269 } 270 } 271 272 Ok(categories.segment_type()) 273} 274 275pub trait GetLiteralValue { 276 fn as_int_literal(&self) -> Option<i64>; 277} 278 279impl GetLiteralValue for crate::ast::Pattern<Arc<Type>> { 280 fn as_int_literal(&self) -> Option<i64> { 281 match self { 282 crate::ast::Pattern::Int { value, .. } => { 283 if let Ok(val) = value.parse::<i64>() { 284 return Some(val); 285 } 286 } 287 crate::ast::Pattern::VarUsage { .. } => return None, 288 _ => (), 289 } 290 None 291 } 292} 293 294fn is_unicode<T>(opt: &BitArrayOption<T>) -> bool { 295 use BitArrayOption::*; 296 297 matches!( 298 opt, 299 Utf8 { .. } 300 | Utf16 { .. } 301 | Utf32 { .. } 302 | Utf8Codepoint { .. } 303 | Utf16Codepoint { .. } 304 | Utf32Codepoint { .. } 305 ) 306} 307 308fn err<A>(error: ErrorType, location: SrcSpan) -> Result<A, Error> { 309 Err(Error { location, error }) 310} 311 312#[derive(Debug)] 313pub struct Error { 314 pub location: SrcSpan, 315 pub error: ErrorType, 316} 317 318#[derive(Debug, PartialEq, Eq, Clone)] 319pub enum ErrorType { 320 ConflictingEndiannessOptions { existing_endianness: EcoString }, 321 ConflictingSignednessOptions { existing_signed: EcoString }, 322 ConflictingSizeOptions, 323 ConflictingTypeOptions { existing_type: EcoString }, 324 ConflictingUnitOptions, 325 FloatWithSize, 326 InvalidEndianness, 327 OptionNotAllowedInValue, 328 SegmentMustHaveSize, 329 SignednessUsedOnNonInt { typ: EcoString }, 330 TypeDoesNotAllowSize { typ: EcoString }, 331 TypeDoesNotAllowUnit { typ: EcoString }, 332 UnitMustHaveSize, 333 VariableUtfSegmentInPattern, 334}