From c952b6d3f507d30c57ffd200a21cddbf18364300 Mon Sep 17 00:00:00 2001 From: Lemuel DLS <26912197+lemueldls@users.noreply.github.com> Date: Mon, 26 Aug 2024 04:24:20 -0400 Subject: [PATCH] Initial RegExp (#184) - Add `RegExp` structure - Add `RegExp.prototype.exec` custom logic - Add new specification tests - Don't print [null] as prototype of object --------- Co-authored-by: Ben --- Cargo.lock | 71 +++++ checker/Cargo.toml | 1 + checker/definitions/internal.ts.d.bin | Bin 31881 -> 32979 bytes checker/definitions/overrides.d.ts | 31 ++ checker/specification/specification.md | 63 +++- checker/src/context/environment.rs | 3 + checker/src/context/root.rs | 1 + checker/src/diagnostics.rs | 24 +- checker/src/features/constant_functions.rs | 59 +++- checker/src/features/mod.rs | 1 + checker/src/features/objects.rs | 5 +- checker/src/features/regexp.rs | 326 +++++++++++++++++++++ checker/src/synthesis/expressions.rs | 25 +- checker/src/types/calling.rs | 1 + checker/src/types/printing.rs | 15 +- checker/src/types/store.rs | 16 +- checker/src/types/subtyping.rs | 9 +- checker/src/utilities/serialization.rs | 10 + 18 files changed, 621 insertions(+), 40 deletions(-) create mode 100644 checker/src/features/regexp.rs diff --git a/Cargo.lock b/Cargo.lock index ecdc9319..81fcb536 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "argh" version = "0.1.12" @@ -312,6 +330,7 @@ dependencies = [ "match_deref", "ordered-float", "path-absolutize", + "regress", "serde", "simple-json-parser", "source-map", @@ -449,6 +468,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + [[package]] name = "inotify" version = "0.9.6" @@ -576,6 +605,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + [[package]] name = "mio" version = "0.8.11" @@ -768,6 +803,16 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regress" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16fe0a24af5daaae947294213d2fd2646fbf5e1fbacc1d4ba3e84b2393854842" +dependencies = [ + "hashbrown", + "memchr", +] + [[package]] name = "rgb" version = "0.8.37" @@ -1059,6 +1104,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -1347,3 +1398,23 @@ name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] diff --git a/checker/Cargo.toml b/checker/Cargo.toml index 6800f69d..d454c225 100644 --- a/checker/Cargo.toml +++ b/checker/Cargo.toml @@ -39,6 +39,7 @@ path-absolutize = { version = "3.0", features = ["use_unix_paths_on_wasm"] } either = "1.6" levenshtein = "1" ordered-float = "4.2" +regress = { version = "0.10.0", features = [] } serde = { version = "1.0", features = ["derive"], optional = true } simple-json-parser = "0.0.2" diff --git a/checker/definitions/internal.ts.d.bin b/checker/definitions/internal.ts.d.bin index 70374b16b94a3215264b90eba49e27ecf21d4883..1460481edadbb2387ebeb48b0b54c4599664480c 100644 GIT binary patch literal 32979 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 Iu|w+rABD=tQUCw| diff --git a/checker/definitions/overrides.d.ts b/checker/definitions/overrides.d.ts index 41c584eb..45ea44d4 100644 --- a/checker/definitions/overrides.d.ts +++ b/checker/definitions/overrides.d.ts @@ -174,6 +174,37 @@ declare class String { declare class Promise { } +declare class RegExp { + @Constant("regexp:constructor") + constructor(pattern: string, flags?: string); + + @Constant("regexp:exec") + exec(input: string): RegExpExecArray | null; +} + +// es5 +interface RegExpExecArray extends Array { + /** + * The index of the search at which the result was found. + */ + index: number; + /** + * A copy of the search string. + */ + input: string; + /** + * The first match. This will always be present because `null` will be returned if there are no matches. + */ + 0: string; +} + +// es2018 +interface RegExpExecArray { + groups?: { + [key: string]: string; + }; +} + type ResponseBody = string; declare class Response { diff --git a/checker/specification/specification.md b/checker/specification/specification.md index 35251578..faaf8845 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -344,8 +344,8 @@ declare type U = { a: 2 } & { b: 3 } declare let x: U; x.b satisfies 3; -({ a: 2, b: 3 } satisfies U); -({ b: 3 } satisfies U); +({ a: 2, b: 3 } satisfies U); +({ b: 3 } satisfies U); ``` - Expected U, found { b: 3 } @@ -2433,7 +2433,7 @@ interface X { - Expected 4, found 5 - Type { a: 3 } is not assignable to type X -#### RegExp +#### `RegExp` constructor > RegExp = Regular expression > In the future, their definition could be considered and evaluated at runtime @@ -2444,6 +2444,55 @@ const regexp = /hi/ satisfies string; - Expected string, found /hi/ +#### Invalid regular expressions + +```ts +const regexp1 = /(?a2)/; +const regexp2 = new RegExp("?a2"); +``` + +- Invalid regular expression: Invalid token at named capture group identifier +- Invalid regular expression: Invalid atom character + +#### Constant `RegExp.exec` + +```ts +const regexp = /hi/; +const match = regexp.exec("hi"); +match satisfies number; +match.index satisfies string; +match.input satisfies boolean; +``` + +- Expected number, found ["hi"] +- Expected string, found 0 +- Expected boolean, found "hi" + +#### Constant `RegExp.exec` groups + +```ts +const regexp = /Hi (?.*)/; +const match = regexp.exec("Hi Ben"); +match.input satisfies number; +match.groups satisfies string; +``` + +- Expected number, found "Hi Ben" +- Expected string, found { name: "Ben" } + +#### Constant `RegExp.exec` groups greedy + +```ts +const regexp = /.*(?[a-z]+)(?[0-9]+)/; +const match = regexp.exec("ez as abc123"); +match.input satisfies number; +match.groups.x satisfies "c"; +match.groups.y satisfies boolean; +``` + +- Expected number, found "ez as abc123" +- Expected boolean, found "123" + #### Null and undefined ```ts @@ -3921,7 +3970,7 @@ function booleanNarrow(param: boolean) { function operatorNarrows(thing: string | null) { (thing ?? "something") satisfies string; (thing || "something") satisfies number; - + const result = thing === "hi" && (thing satisfies boolean); } ``` @@ -3936,7 +3985,7 @@ function logicNarrow(thing: any, other: any) { if (typeof thing === "string" && other === 4) { ({ thing, other }) satisfies string; } - + if (typeof thing === "string" || typeof thing === "number") { thing satisfies null; } @@ -4258,10 +4307,10 @@ proxy1.a satisfies string; ```ts let lastSet: string = ""; -const proxy1 = new Proxy({ a: 2 }, { +const proxy1 = new Proxy({ a: 2 }, { set(target: { a: number }, prop: string, value: number, receiver) { lastSet = prop; - } + } }); proxy1.a = 6; diff --git a/checker/src/context/environment.rs b/checker/src/context/environment.rs index ccdff6fd..9c5754d2 100644 --- a/checker/src/context/environment.rs +++ b/checker/src/context/environment.rs @@ -1367,6 +1367,9 @@ impl<'a> Environment<'a> { "Boolean" => { return Ok(TypeId::BOOLEAN_TYPE); } + "RegExp" => { + return Ok(TypeId::REGEXP_TYPE); + } "Function" => { return Ok(TypeId::FUNCTION_TYPE); } diff --git a/checker/src/context/root.rs b/checker/src/context/root.rs index 4c6bf909..81eb951d 100644 --- a/checker/src/context/root.rs +++ b/checker/src/context/root.rs @@ -64,6 +64,7 @@ impl RootContext { ("void".to_owned(), TypeId::VOID_TYPE), ("Array".to_owned(), TypeId::ARRAY_TYPE), ("Promise".to_owned(), TypeId::PROMISE_TYPE), + ("RegExp".to_owned(), TypeId::REGEXP_TYPE), ("ImportMeta".to_owned(), TypeId::IMPORT_META), ("Function".to_owned(), TypeId::FUNCTION_TYPE), ("object".to_owned(), TypeId::OBJECT_TYPE), diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 41b886f2..89d98a0a 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -58,6 +58,11 @@ pub struct TDZ { pub position: SpanWithSource, } +pub struct InvalidRegexp { + pub error: String, + pub position: SpanWithSource, +} + pub struct NotInLoopOrCouldNotFindLabel { pub label: Label, pub position: SpanWithSource, @@ -452,6 +457,7 @@ pub(crate) enum TypeCheckError<'a> { position: SpanWithSource, }, CannotDeleteProperty(CannotDeleteFromError), + InvalidRegexp(InvalidRegexp), } #[allow(clippy::useless_format)] @@ -827,7 +833,7 @@ impl From> for Diagnostic { } => Diagnostic::Position { reason: match property { PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty}"), - PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}'") + PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}'") }, position, kind, @@ -850,7 +856,7 @@ impl From> for Diagnostic { } => Diagnostic::Position { reason: match property { PropertyKeyRepresentation::Type(ty) => format!("Cannot write to property of type {ty} as it is a getter"), - PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}' as it is a getter") + PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to property '{property}' as it is a getter") }, position, kind, @@ -861,12 +867,17 @@ impl From> for Diagnostic { } => Diagnostic::Position { reason: match property { PropertyKeyRepresentation::Type(ty) => format!("Cannot write to non-existent property of type {ty}"), - PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to non-existent property '{property}'") + PropertyKeyRepresentation::StringKey(property) => format!("Cannot write to non-existent property '{property}'") }, position, kind, } - } + }, + TypeCheckError::InvalidRegexp(InvalidRegexp { error, position }) => Diagnostic::Position { + reason: format!("Invalid regular expression: {error}"), + position, + kind, + }, } } } @@ -1176,5 +1187,10 @@ fn function_calling_error_diagnostic( kind, } } + FunctionCallingError::InvalidRegexp(InvalidRegexp { error, position }) => Diagnostic::Position { + reason: format!("Invalid regular expression: {error}"), + position, + kind, + } } } diff --git a/checker/src/features/constant_functions.rs b/checker/src/features/constant_functions.rs index f8411ab9..6f6f3ac8 100644 --- a/checker/src/features/constant_functions.rs +++ b/checker/src/features/constant_functions.rs @@ -6,7 +6,9 @@ use crate::{ events::printing::debug_effects, features::objects::{ObjectBuilder, Proxy}, types::{ - calling::{Callable, FunctionCallingError, SynthesisedArgument, ThisValue}, + calling::{ + Callable, CallingDiagnostics, FunctionCallingError, SynthesisedArgument, ThisValue, + }, logical::{Logical, LogicalOrValid}, printing::print_type, properties::{AccessMode, Descriptor, PropertyKey, Publicity}, @@ -49,7 +51,7 @@ pub(crate) fn call_constant_function( // TODO `mut` for satisfies which needs checking. Also needed for freeze etc environment: &mut Environment, call_site: SpanWithSource, - diagnostics: &mut crate::types::calling::CallingDiagnostics, + diagnostics: &mut CallingDiagnostics, ) -> Result { // crate::utilities::notify!("Calling constant function {} with {:?}", name, arguments); // TODO as parameter @@ -112,7 +114,9 @@ pub(crate) fn call_constant_function( "toUpperCase" => Constant::String(s.to_uppercase()), "toLowerCase" => Constant::String(s.to_lowercase()), "string_length" => Constant::Number( - (s.len() as f64).try_into().map_err(|_| ConstantFunctionError::BadCall)?, + (s.encode_utf16().count() as f64) + .try_into() + .map_err(|_| ConstantFunctionError::BadCall)?, ), _ => unreachable!(), }); @@ -571,6 +575,55 @@ pub(crate) fn call_constant_function( crate::utilities::notify!("TODO JSON:stringify"); Err(ConstantFunctionError::BadCall) } + "regexp:constructor" => { + let pattern = types + .get_type_by_id(arguments.first().unwrap().non_spread_type().expect("pattern")); + let flags = + arguments.get(1).map(|a| types.get_type_by_id(a.non_spread_type().expect("flags"))); + + let Type::Constant(Constant::String(pattern)) = pattern else { + return Err(ConstantFunctionError::BadCall); + }; + let flags = match flags { + Some(flags) => { + let Type::Constant(Constant::String(flags)) = flags else { + return Err(ConstantFunctionError::BadCall); + }; + + Some(flags.clone()) + } + None => None, + }; + + let regexp = types.new_regexp(&pattern.clone(), &flags, &call_site.without_source()); + + match regexp { + Ok(regex) => Ok(ConstantOutput::Value(regex)), + Err(error) => Err(ConstantFunctionError::FunctionCallingError( + FunctionCallingError::InvalidRegexp(crate::diagnostics::InvalidRegexp { + error, + position: call_site, + }), + )), + } + } + "regexp:exec" => { + let this = this_argument.get_passed().map(|t| types.get_type_by_id(t)); + + if let Some(Type::SpecialObject(SpecialObject::RegularExpression(regexp))) = this { + let pattern_type_id = + arguments.first().unwrap().non_spread_type().expect("pattern"); + + Ok(ConstantOutput::Value(regexp.clone().exec( + pattern_type_id, + types, + environment, + call_site, + ))) + } else { + Err(ConstantFunctionError::BadCall) + } + } // "satisfies" => { // let ty = arguments // .first() diff --git a/checker/src/features/mod.rs b/checker/src/features/mod.rs index f998989f..aac3199b 100644 --- a/checker/src/features/mod.rs +++ b/checker/src/features/mod.rs @@ -17,6 +17,7 @@ pub mod modules; pub mod narrowing; pub mod objects; pub mod operations; +pub mod regexp; pub mod template_literal; pub mod variables; diff --git a/checker/src/features/objects.rs b/checker/src/features/objects.rs index 422043c1..dd90301d 100644 --- a/checker/src/features/objects.rs +++ b/checker/src/features/objects.rs @@ -61,10 +61,7 @@ pub enum SpecialObject { /// Needs overrides for calling, getting etc Proxy(Proxy), /// Not a [Constant] as `typeof /hi/ === "object"` and it has state - RegularExpression { - content: TypeId, - // groups: Option, - }, + RegularExpression(super::regexp::RegExp), /// This cannot be a regular object because of is because of let mutations Import(super::modules::Exported), /// Yeah here. Also for classes diff --git a/checker/src/features/regexp.rs b/checker/src/features/regexp.rs new file mode 100644 index 00000000..7797cef9 --- /dev/null +++ b/checker/src/features/regexp.rs @@ -0,0 +1,326 @@ +use regress::{backends, Flags, Regex}; +use source_map::{SourceId, SpanWithSource}; + +use super::objects::ObjectBuilder; +use crate::{ + types::{ + properties::{PropertyKey, PropertyValue, Publicity}, + TypeStore, + }, + BinarySerializable, Constant, Environment, Type, TypeId, +}; + +#[derive(Debug, Clone)] +pub struct RegExp { + source: String, + re: Regex, + groups: u32, + named_group_indices: crate::Map, + flags_unsupported: bool, + used: bool, +} + +impl RegExp { + pub fn new(pattern: &str, flag_options: Option<&str>) -> Result { + let source = if let Some(flag_options) = flag_options { + format!("/{pattern}/{flag_options}") + } else { + format!("/{pattern}/") + }; + + let mut flags = Flags::default(); + let mut flags_unsupported = false; + + if let Some(flag_options) = flag_options { + for flag in flag_options.chars() { + #[allow(clippy::match_same_arms)] + match flag { + 'd' => flags_unsupported = true, // indices for substring matches are not supported + 'g' => flags_unsupported = true, // stateful regex is not supported + 'i' => flags.icase = true, + 'm' => flags.multiline = true, + 's' => flags.dot_all = true, + 'u' => flags.unicode = true, + 'v' => flags.unicode_sets = true, + 'y' => flags_unsupported = true, // sticky search is not supported + _ => panic!("Unknown flag: {flag:?}"), + } + } + } + + let compiled_regex = { + let mut ire = backends::try_parse(pattern.chars().map(u32::from), flags) + .map_err(|err| err.text)?; + if !flags.no_opt { + backends::optimize(&mut ire); + } + + backends::emit(&ire) + }; + + // crate::utilities::notify!("{:?}", compiled_regex); + + // let insns = compiled_regex.insns; + // let brackets = compiled_regex.brackets; + // let start_pred = compiled_regex.start_pred; + // let loops = compiled_regex.loops; + let groups = compiled_regex.groups + 1; + let named_group_indices = + compiled_regex.named_group_indices.iter().map(|(l, r)| (l.clone(), *r)).collect(); + // let flags = compiled_regex.flags; + + let re = Regex::from(compiled_regex); + + Ok(Self { source, re, groups, named_group_indices, flags_unsupported, used: false }) + } + + #[must_use] + pub fn source(&self) -> &str { + &self.source + } + + #[must_use] + pub fn used(&self) -> bool { + self.used + } + + pub(crate) fn exec( + &self, + pattern_type_id: TypeId, + types: &mut TypeStore, + environment: &mut Environment, + call_site: SpanWithSource, + ) -> TypeId { + let pattern_type = types.get_type_by_id(pattern_type_id); + + match (self.flags_unsupported, pattern_type) { + (false, Type::Constant(Constant::String(pattern))) => { + // Needed to not mutually borrow mutable types + let pattern = pattern.clone(); + self.exec_constant(&pattern, pattern_type_id, types, environment, call_site) + } + _ => self.exec_variable(types, environment, call_site), + } + } + + pub(crate) fn exec_constant( + &self, + pattern: &str, + pattern_type_id: TypeId, + types: &mut TypeStore, + environment: &mut Environment, + call_site: SpanWithSource, + ) -> TypeId { + let mut object = + ObjectBuilder::new(Some(TypeId::ARRAY_TYPE), types, call_site, &mut environment.info); + + object.append( + Publicity::Public, + PropertyKey::String("input".into()), + PropertyValue::Value(pattern_type_id), + call_site, + &mut environment.info, + ); + + match self.re.find(pattern) { + Some(match_) => { + { + let index = types.new_constant_type(Constant::Number( + (match_.start() as f64).try_into().unwrap(), + )); + object.append( + Publicity::Public, + PropertyKey::String("index".into()), + PropertyValue::Value(index), + call_site, + &mut environment.info, + ); + } + + for (idx, group) in match_.groups().enumerate() { + let key = PropertyKey::from_usize(idx); + let value = match group { + Some(range) => { + types.new_constant_type(Constant::String(pattern[range].to_string())) + } + None => todo!(), + }; + + object.append( + Publicity::Public, + key, + PropertyValue::Value(value), + call_site, + &mut environment.info, + ); + } + + { + let named_groups = { + let mut named_groups_object = ObjectBuilder::new( + Some(TypeId::NULL_TYPE), + types, + call_site, + &mut environment.info, + ); + + for (name, group) in match_.named_groups() { + let key = PropertyKey::String(name.to_string().into()); + let value = match group { + Some(range) => types.new_constant_type(Constant::String( + pattern[range].to_string(), + )), + None => todo!(), + }; + + named_groups_object.append( + Publicity::Public, + key, + PropertyValue::Value(value), + call_site, + &mut environment.info, + ); + } + + named_groups_object.build_object() + }; + + object.append( + Publicity::Public, + PropertyKey::String("groups".into()), + PropertyValue::Value(named_groups), + call_site, + &mut environment.info, + ); + } + + { + let length = types.new_constant_type(Constant::Number( + f64::from(self.groups).try_into().unwrap(), + )); + + object.append( + Publicity::Public, + PropertyKey::String("length".into()), + PropertyValue::Value(length), + call_site, + &mut environment.info, + ); + } + + object.build_object() + } + None => TypeId::NULL_TYPE, + } + } + + pub(crate) fn exec_variable( + &self, + types: &mut TypeStore, + environment: &mut Environment, + call_site: SpanWithSource, + ) -> TypeId { + let mut object = + ObjectBuilder::new(Some(TypeId::ARRAY_TYPE), types, call_site, &mut environment.info); + + object.append( + Publicity::Public, + PropertyKey::String("input".into()), + PropertyValue::Value(TypeId::STRING_TYPE), + call_site, + &mut environment.info, + ); + + { + object.append( + Publicity::Public, + PropertyKey::String("index".into()), + PropertyValue::Value(TypeId::NUMBER_TYPE), + call_site, + &mut environment.info, + ); + } + + for idx in 0..self.groups { + let key = PropertyKey::from_usize(idx as usize); + + object.append( + Publicity::Public, + key, + PropertyValue::Value(TypeId::STRING_TYPE), + call_site, + &mut environment.info, + ); + } + + { + let named_groups = { + let mut named_groups_object = ObjectBuilder::new( + Some(TypeId::NULL_TYPE), + types, + call_site, + &mut environment.info, + ); + + for name in self.named_group_indices.keys() { + let key = PropertyKey::String(name.to_string().into()); + + named_groups_object.append( + Publicity::Public, + key, + PropertyValue::Value(TypeId::STRING_TYPE), + call_site, + &mut environment.info, + ); + } + + named_groups_object.build_object() + }; + + object.append( + Publicity::Public, + PropertyKey::String("groups".into()), + PropertyValue::Value(named_groups), + call_site, + &mut environment.info, + ); + } + + { + let length = types + .new_constant_type(Constant::Number(f64::from(self.groups).try_into().unwrap())); + + object.append( + Publicity::Public, + PropertyKey::String("length".into()), + PropertyValue::Value(length), + call_site, + &mut environment.info, + ); + } + + types.new_or_type(object.build_object(), TypeId::NULL_TYPE) + } +} + +impl std::fmt::Display for RegExp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.source) + } +} + +// TODO: Optimize +impl BinarySerializable for RegExp { + fn serialize(self, buf: &mut Vec) { + self.source.serialize(buf); + } + + fn deserialize>(iter: &mut I, source_id: SourceId) -> Self { + let source = String::deserialize(iter, source_id); + + let (pattern, flags) = source[1..].rsplit_once('/').unwrap(); + let flags = if flags.is_empty() { None } else { Some(flags) }; + + Self::new(pattern, flags).unwrap() + } +} diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index 8268c85b..619017f2 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -93,6 +93,25 @@ pub(super) fn synthesise_expression( Expression::StringLiteral(value, ..) => { return checking_data.types.new_constant_type(Constant::String(value.clone())) } + Expression::RegexLiteral { pattern, flags, position } => { + let regexp = checking_data.types.new_regexp(pattern, flags, position); + + match regexp { + Ok(regexp) => Instance::RValue(regexp), + Err(error) => { + checking_data.diagnostics_container.add_error( + crate::diagnostics::TypeCheckError::InvalidRegexp( + crate::diagnostics::InvalidRegexp { + error, + position: position.with_source(environment.get_source()), + }, + ), + ); + + return TypeId::ERROR_TYPE; + } + } + } Expression::NumberLiteral(value, ..) => { let not_nan = if let Ok(v) = f64::try_from(value.clone()) { v.try_into().unwrap() @@ -836,12 +855,6 @@ pub(super) fn synthesise_expression( Expression::JSXRoot(jsx_root) => { Instance::RValue(synthesise_jsx_root(jsx_root, environment, checking_data)) } - Expression::RegexLiteral { pattern, flags: _, position: _ } => { - let content = checking_data.types.new_constant_type(Constant::String(pattern.clone())); - Instance::RValue(checking_data.types.register_type(crate::Type::SpecialObject( - crate::types::SpecialObject::RegularExpression { content }, - ))) - } Expression::Comment { on, .. } => { return synthesise_expression(on, environment, checking_data, expecting); } diff --git a/checker/src/types/calling.rs b/checker/src/types/calling.rs index e3b93222..c7ae9e68 100644 --- a/checker/src/types/calling.rs +++ b/checker/src/types/calling.rs @@ -1012,6 +1012,7 @@ pub enum FunctionCallingError { /// Should be set by parent call_site: SpanWithSource, }, + InvalidRegexp(crate::diagnostics::InvalidRegexp), /// For #18 SetPropertyConstraint { property_type: TypeStringRepresentation, diff --git a/checker/src/types/printing.rs b/checker/src/types/printing.rs index 91307e3d..0490fae6 100644 --- a/checker/src/types/printing.rs +++ b/checker/src/types/printing.rs @@ -632,7 +632,9 @@ pub fn print_type_into_buf( } } } else { - if let Some(prototype) = prototype { + if let Some(prototype) = + prototype.filter(|prototype| !matches!(*prototype, TypeId::NULL_TYPE)) + { // crate::utilities::notify!("P during print {:?}", prototype); buf.push('['); print_type_into_buf(prototype, buf, cycles, args, types, info, debug); @@ -799,15 +801,8 @@ pub fn print_type_into_buf( } buf.push_str(" }"); } - SpecialObject::RegularExpression { content, .. } => { - // TODO flags - if let Type::Constant(crate::Constant::String(name)) = - types.get_type_by_id(*content) - { - write!(buf, "/{name}/").unwrap(); - } else { - buf.push_str("RegExp"); - } + SpecialObject::RegularExpression(exp) => { + buf.push_str(exp.source()); } SpecialObject::Function(..) => unreachable!(), }, diff --git a/checker/src/types/store.rs b/checker/src/types/store.rs index 07662858..a72698fc 100644 --- a/checker/src/types/store.rs +++ b/checker/src/types/store.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; -use crate::{types::intrinsics::Intrinsic, Constant, Map as SmallMap}; -use source_map::{Nullable, SpanWithSource}; +use crate::{features::regexp::RegExp, types::intrinsics::Intrinsic, Constant, Map as SmallMap}; +use source_map::{Nullable, Span, SpanWithSource}; use crate::{ features::{functions::ClosureId, objects::SpecialObject}, @@ -491,6 +491,18 @@ impl TypeStore { } } + pub fn new_regexp( + &mut self, + pattern: &str, + flags: &Option, + _position: &Span, + ) -> Result { + let regexp = RegExp::new(pattern, flags.as_ref().map(String::as_str))?; + let ty = Type::SpecialObject(SpecialObject::RegularExpression(regexp)); + + Ok(self.register_type(ty)) + } + pub fn new_function_parameter(&mut self, parameter_constraint: TypeId) -> TypeId { // TODO this has problems if there are two generic types. Aka `(a: T, b: T) -> T`. Although I have // no idea why this is possible so should be fine? diff --git a/checker/src/types/subtyping.rs b/checker/src/types/subtyping.rs index 3fa83b11..f845791e 100644 --- a/checker/src/types/subtyping.rs +++ b/checker/src/types/subtyping.rs @@ -1476,7 +1476,11 @@ pub(crate) fn type_is_subtype_with_generics( types, ) } - Type::AliasTo { .. } | Type::Interface { .. } => { + Type::FunctionReference(_) + | Type::SpecialObject(_) + | Type::Class { .. } + | Type::AliasTo { .. } + | Type::Interface { .. } => { crate::utilities::notify!("lhs={:?} rhs={:?}", left_ty, right_ty); // TODO SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) @@ -1519,9 +1523,6 @@ pub(crate) fn type_is_subtype_with_generics( // ) // } } - Type::FunctionReference(_) => todo!(), - Type::SpecialObject(_) => todo!(), - Type::Class { .. } => todo!(), } } Type::SpecialObject(SpecialObject::Null) => { diff --git a/checker/src/utilities/serialization.rs b/checker/src/utilities/serialization.rs index 654727b2..60d6e8d7 100644 --- a/checker/src/utilities/serialization.rs +++ b/checker/src/utilities/serialization.rs @@ -198,6 +198,16 @@ impl BinarySerializable for SourceId { } } +impl BinarySerializable for u16 { + fn serialize(self, buf: &mut Vec) { + buf.extend_from_slice(&self.to_le_bytes()); + } + + fn deserialize>(iter: &mut I, _source: SourceId) -> Self { + u16::from_le_bytes([iter.next().unwrap(), iter.next().unwrap()]) + } +} + impl BinarySerializable for u32 { fn serialize(self, buf: &mut Vec) { buf.extend_from_slice(&self.to_le_bytes());