diff --git a/crates/erg_compiler/link_hir.rs b/crates/erg_compiler/link_hir.rs index 86f1c5a76..f7831d38b 100644 --- a/crates/erg_compiler/link_hir.rs +++ b/crates/erg_compiler/link_hir.rs @@ -233,6 +233,11 @@ impl<'a> HIRLinker<'a> { match acc { Accessor::Attr(attr) => { self.replace_import(&mut attr.obj); + if attr.ident.inspect() == "__file__" + && attr.ident.vi.def_loc.module.is_none() + { + *expr = self.__file__(); + } } Accessor::Ident(ident) => match &ident.inspect()[..] { "module" => { @@ -241,6 +246,9 @@ impl<'a> HIRLinker<'a> { "global" => { *expr = Expr::from(Identifier::static_public("__builtins__")); } + "__file__" if ident.vi.def_loc.module.is_none() => { + *expr = self.__file__(); + } _ => {} }, } @@ -363,6 +371,22 @@ impl<'a> HIRLinker<'a> { Expr::from(__import__).call1(Expr::from(__name__)) } + fn __file__(&self) -> Expr { + let path = self.cfg.input.path().to_path_buf(); + let token = Token::new_fake( + TokenKind::StrLit, + format!( + "\"{}\"", + path.canonicalize().unwrap_or(path).to_string_lossy() + ), + 0, + 0, + 0, + ); + let lit = Literal::try_from(token).unwrap(); + Expr::from(lit) + } + /// ```erg /// x = import "mod" /// ``` diff --git a/tests/should_ok/dunder.er b/tests/should_ok/dunder.er new file mode 100644 index 000000000..c1a94b314 --- /dev/null +++ b/tests/should_ok/dunder.er @@ -0,0 +1,15 @@ +imp = import "import" + +func() = + __file__ = "foo" + __file__ + +C = Class() +C. + __file__ = "bar" + +assert __file__.endswith "dunder.er" +assert func() == "foo" +assert module::__file__.endswith "dunder.er" +assert C.new().__file__ == "bar" +assert imp.func().endswith "import.er" diff --git a/tests/should_ok/import.er b/tests/should_ok/import.er new file mode 100644 index 000000000..1839979c8 --- /dev/null +++ b/tests/should_ok/import.er @@ -0,0 +1 @@ +.func() = __file__ diff --git a/tests/test.rs b/tests/test.rs index 0ab2833ac..df040afb8 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -131,6 +131,11 @@ fn exec_dict_test() -> Result<(), ()> { expect_success("tests/should_ok/dict.er", 0) } +#[test] +fn exec_dunder() -> Result<(), ()> { + expect_success("tests/should_ok/dunder.er", 0) +} + #[test] fn exec_empty_check() -> Result<(), ()> { expect_success("tests/should_ok/dyn_type_check.er", 0)