diff --git a/rust/src/analyzer/literals.rs b/rust/src/analyzer/literals.rs index 98f3995..60e0d89 100644 --- a/rust/src/analyzer/literals.rs +++ b/rust/src/analyzer/literals.rs @@ -1,7 +1,7 @@ //! Literal Handlers - Processing Ruby literal values //! //! This module is responsible for: -//! - String, Integer, Float, Hash, Regexp literals +//! - String, Integer, Float, Hash, Regexp, Range literals //! - nil, true, false, Symbol literals //! - Creating Source vertices with fixed types //! @@ -62,6 +62,11 @@ pub fn install_literal(genv: &mut GlobalEnv, node: &Node) -> Option { return Some(genv.new_source(Type::regexp())); } + // 1..5, "a".."z" (Range literal) + if node.as_range_node().is_some() { + return Some(genv.new_source(Type::range())); + } + None } @@ -102,4 +107,12 @@ mod tests { let vtx = genv.new_source(Type::regexp()); assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Regexp"); } + + #[test] + fn test_install_range_literal() { + let mut genv = GlobalEnv::new(); + + let vtx = genv.new_source(Type::range()); + assert_eq!(genv.get_source(vtx).unwrap().ty.show(), "Range"); + } } diff --git a/rust/src/analyzer/tests/integration_test.rs b/rust/src/analyzer/tests/integration_test.rs index 3898940..a30f310 100644 --- a/rust/src/analyzer/tests/integration_test.rs +++ b/rust/src/analyzer/tests/integration_test.rs @@ -39,6 +39,15 @@ fn analyze(source: &str) -> (GlobalEnv, LocalEnv) { genv.register_builtin_method(Type::regexp(), "match?", Type::instance("TrueClass")); genv.register_builtin_method(Type::regexp(), "source", Type::string()); + // Register Range methods + genv.register_builtin_method(Type::range(), "to_a", Type::array()); + genv.register_builtin_method(Type::range(), "size", Type::integer()); + genv.register_builtin_method(Type::range(), "count", Type::integer()); + genv.register_builtin_method(Type::range(), "first", Type::Bot); + genv.register_builtin_method(Type::range(), "last", Type::Bot); + genv.register_builtin_method(Type::range(), "include?", Type::instance("TrueClass")); + genv.register_builtin_method(Type::range(), "cover?", Type::instance("TrueClass")); + let mut lenv = LocalEnv::new(); let mut installer = AstInstaller::new(&mut genv, &mut lenv, source); @@ -525,3 +534,73 @@ a = x.source let a_vtx = lenv.get_var("a").unwrap(); assert_eq!(genv.get_vertex(a_vtx).unwrap().show(), "String"); } + +// ============================================ +// Range Literal Tests +// ============================================ + +#[test] +fn test_range_literal_basic() { + let source = r#"x = 1..5"#; + + let (genv, lenv) = analyze(source); + + let x_vtx = lenv.get_var("x").unwrap(); + assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range"); +} + +#[test] +fn test_range_literal_exclusive() { + let source = r#"x = 1...5"#; + + let (genv, lenv) = analyze(source); + + let x_vtx = lenv.get_var("x").unwrap(); + assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range"); +} + +#[test] +fn test_range_literal_string() { + let source = r#"x = "a".."z""#; + + let (genv, lenv) = analyze(source); + + let x_vtx = lenv.get_var("x").unwrap(); + assert_eq!(genv.get_vertex(x_vtx).unwrap().show(), "Range"); +} + +#[test] +fn test_range_literal_type_error() { + let source = r#" +class Calculator + def compute + x = 1..10 + y = x.upcase + end +end +"#; + + let (genv, _lenv) = analyze(source); + + assert_eq!(genv.type_errors.len(), 1); + assert_eq!(genv.type_errors[0].method_name, "upcase"); +} + +#[test] +fn test_range_specific_methods() { + let source = r#" +x = 1..10 +a = x.to_a +b = x.size +"#; + + let (genv, lenv) = analyze(source); + + assert_eq!(genv.type_errors.len(), 0); + + let a_vtx = lenv.get_var("a").unwrap(); + assert_eq!(genv.get_vertex(a_vtx).unwrap().show(), "Array"); + + let b_vtx = lenv.get_var("b").unwrap(); + assert_eq!(genv.get_vertex(b_vtx).unwrap().show(), "Integer"); +} diff --git a/rust/src/types.rs b/rust/src/types.rs index 4580a1c..081592b 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -248,6 +248,12 @@ impl Type { } } + pub fn range() -> Self { + Type::Instance { + name: QualifiedName::simple("Range"), + } + } + /// Create a generic Array type: Array[element_type] pub fn array_of(element_type: Type) -> Self { Type::Generic {