⭐️ A friendly language for building type-safe, scalable systems!
at main 11 kB view raw
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 if 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 222 // Endianness is only valid for int, utf16, utf32 and float 223 match categories { 224 SegmentOptionCategories { 225 type_: None | Some(Int { .. } | Utf16 { .. } | Utf32 { .. } | Float { .. }), 226 .. 227 } => {} 228 229 SegmentOptionCategories { 230 endian: Some(endian), 231 .. 232 } => return err(ErrorType::InvalidEndianness, endian.location()), 233 234 _ => {} 235 } 236 237 // signed and unsigned can only be used with int types 238 match categories { 239 SegmentOptionCategories { 240 type_: None | Some(Int { .. }), 241 .. 242 } => {} 243 244 SegmentOptionCategories { 245 type_: Some(opt), 246 signed: Some(sign), 247 .. 248 } => { 249 return err( 250 ErrorType::SignednessUsedOnNonInt { type_: opt.label() }, 251 sign.location(), 252 ); 253 } 254 255 _ => {} 256 } 257 258 // utf8, utf16, utf32 exclude unit and size 259 match categories { 260 SegmentOptionCategories { 261 type_: Some(type_), 262 unit: Some(_), 263 .. 264 } if is_unicode(type_) => { 265 return err( 266 ErrorType::TypeDoesNotAllowUnit { 267 type_: type_.label(), 268 }, 269 type_.location(), 270 ); 271 } 272 273 SegmentOptionCategories { 274 type_: Some(type_), 275 size: Some(_), 276 .. 277 } if is_unicode(type_) => { 278 return err( 279 ErrorType::TypeDoesNotAllowSize { 280 type_: type_.label(), 281 }, 282 type_.location(), 283 ); 284 } 285 286 _ => {} 287 } 288 289 // if unit specified, size must be specified 290 if let SegmentOptionCategories { 291 unit: Some(unit), 292 size: None, 293 .. 294 } = categories 295 { 296 return err(ErrorType::UnitMustHaveSize, unit.location()); 297 } 298 299 // float only 16/32/64 300 if let SegmentOptionCategories { 301 type_: Some(Float { .. }), 302 size: Some(size), 303 .. 304 } = categories 305 { 306 if let Some(abox) = size.value() { 307 match abox.as_int_literal() { 308 None => (), 309 Some(value) if value == 16.into() || value == 32.into() || value == 64.into() => (), 310 _ => return err(ErrorType::FloatWithSize, size.location()), 311 } 312 } 313 } 314 315 // Segment patterns with a zero or negative constant size must be rejected, 316 // we know they will never match! 317 // A negative size is still allowed in expressions as it will just result 318 // in an empty segment. 319 if let (Some(size @ Size { value, .. }), TypeOptionsMode::Pattern) = (categories.size, mode) { 320 match value.as_int_literal() { 321 Some(n) if n <= BigInt::ZERO => { 322 return err(ErrorType::ConstantSizeNotPositive, size.location()); 323 } 324 Some(_) | None => (), 325 } 326 } 327 328 Ok(categories.segment_type()) 329} 330 331pub trait GetLiteralValue { 332 fn as_int_literal(&self) -> Option<BigInt>; 333} 334 335impl GetLiteralValue for ast::TypedPattern { 336 fn as_int_literal(&self) -> Option<BigInt> { 337 if let ast::Pattern::Int { int_value, .. } = self { 338 Some(int_value.clone()) 339 } else { 340 None 341 } 342 } 343} 344 345fn is_unicode<T>(opt: &BitArrayOption<T>) -> bool { 346 use BitArrayOption::*; 347 348 matches!( 349 opt, 350 Utf8 { .. } 351 | Utf16 { .. } 352 | Utf32 { .. } 353 | Utf8Codepoint { .. } 354 | Utf16Codepoint { .. } 355 | Utf32Codepoint { .. } 356 ) 357} 358 359fn err<A>(error: ErrorType, location: SrcSpan) -> Result<A, Error> { 360 Err(Error { location, error }) 361} 362 363#[derive(Debug)] 364pub struct Error { 365 pub location: SrcSpan, 366 pub error: ErrorType, 367} 368 369#[derive(Debug, PartialEq, Eq, Clone)] 370pub enum ErrorType { 371 ConflictingEndiannessOptions { 372 existing_endianness: EcoString, 373 }, 374 ConflictingSignednessOptions { 375 existing_signed: EcoString, 376 }, 377 ConflictingSizeOptions, 378 ConflictingTypeOptions { 379 existing_type: EcoString, 380 }, 381 ConflictingUnitOptions, 382 FloatWithSize, 383 InvalidEndianness, 384 OptionNotAllowedInValue, 385 SegmentMustHaveSize, 386 SignednessUsedOnNonInt { 387 type_: EcoString, 388 }, 389 TypeDoesNotAllowSize { 390 type_: EcoString, 391 }, 392 TypeDoesNotAllowUnit { 393 type_: EcoString, 394 }, 395 UnitMustHaveSize, 396 VariableUtfSegmentInPattern, 397 ConstantSizeNotPositive, 398 OptionNotSupportedForTarget { 399 target: Target, 400 option: UnsupportedOption, 401 }, 402} 403 404#[derive(Debug, PartialEq, Eq, Clone, Copy)] 405pub enum UnsupportedOption { 406 UtfCodepointPattern, 407 NativeEndianness, 408}