diff --git a/crates/erg_common/config.rs b/crates/erg_common/config.rs index 6d8e1c3f0..57358f4f9 100644 --- a/crates/erg_common/config.rs +++ b/crates/erg_common/config.rs @@ -12,6 +12,7 @@ use std::str::FromStr; use crate::help_messages::{command_message, mode_message}; use crate::normalize_path; use crate::python_util::{detect_magic_number, get_python_version, PythonVersion}; +use crate::random::random; use crate::serialize::{get_magic_num_from_bytes, get_ver_from_magic_num}; use crate::stdin::GLOBAL_STDIN; use crate::{power_assert, read_file}; @@ -111,18 +112,30 @@ impl DummyStdin { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Input { File(PathBuf), - REPL, + REPL(u64), DummyREPL(DummyStdin), /// same content as cfg.command - Pipe(String), + Pipe(u64, String), /// from command option | eval - Str(String), + Str(u64, String), Dummy, } impl Input { - pub fn is_repl(&self) -> bool { - matches!(self, Input::REPL | Input::DummyREPL(_)) + pub const fn is_repl(&self) -> bool { + matches!(self, Input::REPL(_) | Input::DummyREPL(_)) + } + + pub fn pipe(src: String) -> Self { + Self::Pipe(random(), src) + } + + pub fn str(src: String) -> Self { + Self::Str(random(), src) + } + + pub fn repl() -> Self { + Self::REPL(random()) } pub fn path(&self) -> Option<&Path> { @@ -132,40 +145,57 @@ impl Input { } } + pub const fn id(&self) -> u64 { + match self { + Input::File(_) | Input::DummyREPL(_) | Input::Dummy => 0, + Input::REPL(id) | Input::Pipe(id, _) | Input::Str(id, _) => *id, + } + } + pub fn enclosed_name(&self) -> &str { match self { Self::File(filename) => filename.to_str().unwrap_or("_"), - Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "", - Self::Str(_) => "", + Self::REPL(_) | Self::DummyREPL(_) | Self::Pipe(_, _) => "", + Self::Str(_, _) => "", Self::Dummy => "", } } - pub fn full_path(&self) -> &str { + pub fn full_path(&self) -> PathBuf { match self { - Self::File(filename) => filename.to_str().unwrap_or("_"), - Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "stdin", - Self::Str(_) => "string", - Self::Dummy => "dummy", + Self::File(filename) => filename.clone(), + Self::REPL(id) | Self::Pipe(id, _) => PathBuf::from(format!("stdin_{id}")), + Self::DummyREPL(dummy) => PathBuf::from(format!("stdin_{}", dummy.name)), + Self::Str(id, _) => PathBuf::from(format!("string_{id}")), + Self::Dummy => PathBuf::from("dummy"), } } - pub fn file_stem(&self) -> &str { + pub fn file_stem(&self) -> String { match self { - Self::File(filename) => filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_"), - Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "stdin", - Self::Str(_) => "string", - Self::Dummy => "dummy", + Self::File(filename) => filename + .file_stem() + .and_then(|f| f.to_str()) + .unwrap_or("_") + .to_string(), + Self::REPL(id) | Self::Pipe(id, _) => format!("stdin_{id}"), + Self::DummyREPL(stdin) => format!("stdin_{}", stdin.name), + Self::Str(id, _) => format!("string_{id}"), + Self::Dummy => "dummy".to_string(), } } - pub fn filename(&self) -> &str { + pub fn filename(&self) -> String { match self { - Self::File(filename) => filename.file_name().and_then(|f| f.to_str()).unwrap_or("_"), - Self::REPL | Self::Pipe(_) => "stdin", - Self::DummyREPL(stdin) => &stdin.name, - Self::Str(_) => "string", - Self::Dummy => "dummy", + Self::File(filename) => filename + .file_name() + .and_then(|f| f.to_str()) + .unwrap_or("_") + .to_string(), + Self::REPL(id) | Self::Pipe(id, _) => format!("stdin_{id}"), + Self::DummyREPL(stdin) => format!("stdin_{}", stdin.name), + Self::Str(id, _) => format!("string_{id}"), + Self::Dummy => "dummy".to_string(), } } @@ -193,8 +223,8 @@ impl Input { } } } - Self::Pipe(s) | Self::Str(s) => s.clone(), - Self::REPL => GLOBAL_STDIN.read(), + Self::Pipe(_, s) | Self::Str(_, s) => s.clone(), + Self::REPL(_) => GLOBAL_STDIN.read(), Self::DummyREPL(dummy) => dummy.read_line().unwrap_or_default(), Self::Dummy => panic!("cannot read from a dummy file"), } @@ -224,8 +254,8 @@ impl Input { } } } - Self::Pipe(s) | Self::Str(s) => s.clone(), - Self::REPL => GLOBAL_STDIN.read(), + Self::Pipe(_, s) | Self::Str(_, s) => s.clone(), + Self::REPL(_) => GLOBAL_STDIN.read(), Self::Dummy | Self::DummyREPL(_) => panic!("cannot read from a dummy file"), } } @@ -244,12 +274,12 @@ impl Input { } Err(_) => vec!["".into()], }, - Self::Pipe(s) | Self::Str(s) => s.split('\n').collect::>() + Self::Pipe(_, s) | Self::Str(_, s) => s.split('\n').collect::>() [ln_begin - 1..=ln_end - 1] .iter() .map(|s| s.to_string()) .collect(), - Self::REPL => GLOBAL_STDIN.reread_lines(ln_begin, ln_end), + Self::REPL(_) => GLOBAL_STDIN.reread_lines(ln_begin, ln_end), Self::DummyREPL(dummy) => dummy.reread_lines(ln_begin, ln_end), Self::Dummy => panic!("cannot read lines from a dummy file"), } @@ -258,8 +288,8 @@ impl Input { pub fn reread(&self) -> String { match self { Self::File(_filename) => todo!(), - Self::Pipe(s) | Self::Str(s) => s.clone(), - Self::REPL => GLOBAL_STDIN.reread().trim_end().to_owned(), + Self::Pipe(_, s) | Self::Str(_, s) => s.clone(), + Self::REPL(_) => GLOBAL_STDIN.reread().trim_end().to_owned(), Self::DummyREPL(dummy) => dummy.reread().unwrap_or_default(), Self::Dummy => panic!("cannot read from a dummy file"), } @@ -366,7 +396,7 @@ impl Default for ErgConfig { py_server_timeout: 10, quiet_repl: false, show_type: false, - input: Input::REPL, + input: Input::repl(), output_dir: None, module: "", verbose: 1, @@ -393,11 +423,11 @@ impl ErgConfig { self.clone() } - pub fn dump_path(&self) -> String { + pub fn dump_path(&self) -> PathBuf { if let Some(output) = &self.output_dir { - format!("{output}/{}", self.input.filename()) + PathBuf::from(format!("{output}/{}", self.input.filename())) } else { - self.input.full_path().to_string() + self.input.full_path() } } @@ -405,17 +435,14 @@ impl ErgConfig { if let Some(output) = &self.output_dir { format!("{output}/{}", self.input.filename()) } else { - self.input.filename().to_string() + self.input.filename() } } - pub fn dump_pyc_path(&self) -> String { - let dump_path = self.dump_path(); - if dump_path.ends_with(".er") { - dump_path.replace(".er", ".pyc") - } else { - dump_path + ".pyc" - } + pub fn dump_pyc_path(&self) -> PathBuf { + let mut dump_path = self.dump_path(); + dump_path.set_extension(".pyc"); + dump_path } pub fn dump_pyc_filename(&self) -> String { @@ -450,7 +477,7 @@ impl ErgConfig { break; } "-c" | "--code" => { - cfg.input = Input::Str(args.next().expect("the value of `-c` is not passed")); + cfg.input = Input::str(args.next().expect("the value of `-c` is not passed")); } "--check" => { cfg.mode = ErgMode::FullCheck; @@ -630,15 +657,15 @@ USAGE: } } } - if cfg.input == Input::REPL && cfg.mode != ErgMode::LanguageServer { + if cfg.input.is_repl() && cfg.mode != ErgMode::LanguageServer { use crate::tty::IsTty; let is_stdin_piped = !stdin().is_tty(); let input = if is_stdin_piped { let mut buffer = String::new(); stdin().read_to_string(&mut buffer).unwrap(); - Input::Pipe(buffer) + Input::pipe(buffer) } else { - Input::REPL + Input::repl() }; cfg.input = input; } diff --git a/crates/erg_common/lib.rs b/crates/erg_common/lib.rs index ab34f841b..2ed1f47f4 100644 --- a/crates/erg_common/lib.rs +++ b/crates/erg_common/lib.rs @@ -21,6 +21,7 @@ pub mod opcode308; pub mod opcode310; pub mod opcode311; pub mod python_util; +pub mod random; pub mod serialize; pub mod set; pub mod shared; diff --git a/crates/erg_common/random.rs b/crates/erg_common/random.rs new file mode 100644 index 000000000..e55052878 --- /dev/null +++ b/crates/erg_common/random.rs @@ -0,0 +1,7 @@ +use std::collections::hash_map::RandomState; +use std::hash::{BuildHasher, Hasher}; + +pub fn random() -> u64 { + let state = RandomState::new(); + state.build_hasher().finish() +} diff --git a/crates/erg_common/traits.rs b/crates/erg_common/traits.rs index 1205f2d01..79ddb4517 100644 --- a/crates/erg_common/traits.rs +++ b/crates/erg_common/traits.rs @@ -577,8 +577,8 @@ pub trait Runnable: Sized + Default { let mut num_errors = 0; let mut instance = Self::new(cfg); let res = match instance.input() { - Input::File(_) | Input::Pipe(_) | Input::Str(_) => instance.exec(), - Input::REPL | Input::DummyREPL(_) => { + Input::File(_) | Input::Pipe(_, _) | Input::Str(_, _) => instance.exec(), + Input::REPL(_) | Input::DummyREPL(_) => { let output = stdout(); let mut output = BufWriter::new(output.lock()); if !quiet_repl { diff --git a/crates/erg_compiler/build_hir.rs b/crates/erg_compiler/build_hir.rs index da0d63eb2..a9b2cc839 100644 --- a/crates/erg_compiler/build_hir.rs +++ b/crates/erg_compiler/build_hir.rs @@ -83,7 +83,7 @@ impl Runnable for HIRBuilder { impl Buildable for HIRBuilder { fn inherit(cfg: ErgConfig, shared: SharedCompilerResource) -> Self { - let mod_name = Str::rc(cfg.input.file_stem()); + let mod_name = Str::rc(&cfg.input.file_stem()); Self::new_with_cache(cfg, mod_name, shared) } fn build(&mut self, src: String, mode: &str) -> Result { diff --git a/crates/erg_compiler/error/mod.rs b/crates/erg_compiler/error/mod.rs index 04444a6a1..f327c9846 100644 --- a/crates/erg_compiler/error/mod.rs +++ b/crates/erg_compiler/error/mod.rs @@ -528,18 +528,18 @@ mod test { fn default_error_format_confirmation() { let mut errors = Vec::new(); - let input = Input::Pipe("stack bug error".to_owned()); + let input = Input::pipe("stack bug error".to_owned()); let loc = Location::Line(1); let err = CompileError::stack_bug(input, loc, 0, 0, "FileName"); errors.push(err); - let input = Input::Pipe("checker bug error".to_owned()); + let input = Input::pipe("checker bug error".to_owned()); let errno = 0; let err = TyCheckError::checker_bug(input, errno, Location::Unknown, "name", 1); errors.push(err); let loc = Location::LineRange(1, 3); - let input = Input::Pipe("args\nmissing\nerror".to_string()); + let input = Input::pipe("args\nmissing\nerror".to_string()); let caused_by = ""; let err = TyCheckError::args_missing_error( input, @@ -559,7 +559,7 @@ mod test { }; let expect = Type::Nat; let found = Type::Int; - let input = Input::Pipe("return type error".to_string()); + let input = Input::pipe("return type error".to_string()); let name = "name"; let err = TyCheckError::return_type_error( input, @@ -580,7 +580,7 @@ mod test { }; let expect = Type::Nat; let found = Type::Int; - let input = Input::Pipe("type mismatch error".to_string()); + let input = Input::pipe("type mismatch error".to_string()); let err = TyCheckError::type_mismatch_error( input, errno, @@ -595,7 +595,7 @@ mod test { ); errors.push(err); - let input = Input::Pipe( + let input = Input::pipe( "too_many_args_error(some_long_name_variable_1, some_long_name_variable_2, some_long_name_variable_3, @@ -619,12 +619,12 @@ mod test { ); errors.push(err); - let input = Input::Pipe("argument error".to_string()); + let input = Input::pipe("argument error".to_string()); let loc = Location::range(1, 0, 1, 8); let err = TyCheckError::argument_error(input, errno, loc, caused_by.to_string(), 1, 2); errors.push(err); - let input = Input::Pipe("Nat <: Int <: Ratio".to_string()); + let input = Input::pipe("Nat <: Int <: Ratio".to_string()); let loc = Location::range(1, 0, 1, 10); let sub_t = &Type::Nat; let sup_t = &Type::Int; @@ -632,14 +632,14 @@ mod test { TyCheckError::subtyping_error(input, errno, sub_t, sup_t, loc, caused_by.to_string()); errors.push(err); - let input = Input::Pipe("pred unification error".to_string()); + let input = Input::pipe("pred unification error".to_string()); let lhs = &Predicate::Const("Str".into()); let rhs = &Predicate::Const("Nat".into()); let err = TyCheckError::pred_unification_error(input, errno, lhs, rhs, caused_by.to_string()); errors.push(err); - let input = Input::Pipe("Trait member type error".to_string()); + let input = Input::pipe("Trait member type error".to_string()); let errno = 0; let loc = Location::Range { ln_begin: 1, @@ -663,7 +663,7 @@ mod test { ); errors.push(err); - let input = Input::Pipe("trait member not defined error".to_string()); + let input = Input::pipe("trait member not defined error".to_string()); let member_name = "member name"; let trait_type = &Type::ClassType; let class_type = &Type::Ellipsis; @@ -679,7 +679,7 @@ mod test { ); errors.push(err); - let input = Input::Pipe("singular no attribute error".to_string()); + let input = Input::pipe("singular no attribute error".to_string()); let loc = Location::Range { ln_begin: 1, col_begin: 0, @@ -702,7 +702,7 @@ mod test { ); errors.push(err); - let input = Input::Pipe("ambiguous type error".to_string()); + let input = Input::pipe("ambiguous type error".to_string()); let expr = Identifier::new( Some(erg_parser::token::Token { kind: erg_parser::token::TokenKind::EOF, @@ -728,7 +728,7 @@ mod test { EvalError::ambiguous_type_error(input, errno, &expr, candidates, caused_by.to_string()); errors.push(err); - let input = Input::Pipe("invalid type cast error".to_string()); + let input = Input::pipe("invalid type cast error".to_string()); let loc = Location::range(1, 8, 1, 17); let cast_to = Type::Error; let hint = Some("hint message here".to_string()); @@ -743,7 +743,7 @@ mod test { ); errors.push(err); - let input = Input::Pipe("override error".to_string()); + let input = Input::pipe("override error".to_string()); let name_loc = Location::range(1, 0, 1, 8); let superclass = &Type::Failure; let err = TyCheckError::override_error( @@ -756,14 +756,14 @@ mod test { ); errors.push(err); - let input = Input::Pipe("visibility error".to_string()); + let input = Input::pipe("visibility error".to_string()); let loc = Location::Line(1); let vis = erg_common::vis::Visibility::Private; let err = TyCheckError::visibility_error(input, errno, loc, caused_by.to_string(), name, vis); errors.push(err); - let input = Input::Pipe("import nunpy as np".to_string()); + let input = Input::pipe("import nunpy as np".to_string()); let errno = 0; let desc = "nunpy is not defined".to_string(); let loc = Location::range(1, 7, 1, 12); diff --git a/crates/erg_compiler/transpile.rs b/crates/erg_compiler/transpile.rs index 6256444c7..583ca9723 100644 --- a/crates/erg_compiler/transpile.rs +++ b/crates/erg_compiler/transpile.rs @@ -143,7 +143,8 @@ impl Runnable for Transpiler { } fn exec(&mut self) -> Result { - let path = self.cfg.dump_path().replace(".er", ".py"); + let mut path = self.cfg.dump_path(); + path.set_extension(".py"); let src = self.cfg.input.read(); let artifact = self.transpile(src, "exec").map_err(|eart| { eart.warns.fmt_all_stderr(); @@ -181,7 +182,7 @@ impl ContextProvider for Transpiler { impl Buildable for Transpiler { fn inherit(cfg: ErgConfig, shared: SharedCompilerResource) -> Self { - let mod_name = Str::rc(cfg.input.file_stem()); + let mod_name = Str::from(cfg.input.file_stem()); Self { shared: shared.clone(), builder: HIRBuilder::new_with_cache(cfg.copy(), mod_name, shared), diff --git a/crates/erg_parser/build_ast.rs b/crates/erg_parser/build_ast.rs index 1ee1125d5..a394ca590 100644 --- a/crates/erg_parser/build_ast.rs +++ b/crates/erg_parser/build_ast.rs @@ -61,13 +61,15 @@ impl ASTBuilder { let module = self.runner.parse(src)?; let mut desugarer = Desugarer::new(); let module = desugarer.desugar(module); - let ast = AST::new(Str::rc(self.runner.cfg().input.full_path()), module); + let path = self.runner.cfg().input.full_path(); + let ast = AST::new(Str::rc(path.to_str().unwrap()), module); Ok(ast) } pub fn build_without_desugaring(&mut self, src: String) -> Result { let module = self.runner.parse(src)?; - let ast = AST::new(Str::rc(self.runner.cfg().input.full_path()), module); + let path = self.runner.cfg().input.full_path(); + let ast = AST::new(Str::rc(path.to_str().unwrap()), module); Ok(ast) } } diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index f9316570b..a9c666113 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -292,7 +292,7 @@ impl ParserRunner { } pub fn parse(&mut self, src: String) -> Result { - let ts = Lexer::new(Input::Str(src)) + let ts = Lexer::new(Input::Str(self.cfg.input.id(), src)) .lex() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; Parser::new(ts)