diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index ffc5173e..efffcf56 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -1522,7 +1522,7 @@ impl Context { } let path = name.split("::").next().unwrap_or(name); let path = path.split('.').next().unwrap_or(path); - let path = self.cfg.input.resolve(Path::new(path)).ok()?; + let path = self.resolve_path(Path::new(path)); if let Some(ctx) = self .mod_cache .as_ref() @@ -1545,7 +1545,7 @@ impl Context { // NOTE: This needs to be changed if we want to be able to define classes/traits outside of the top level let path = name.split("::").next().unwrap_or(name); let path = path.split('.').next().unwrap_or(path); - let path = self.cfg.input.resolve(Path::new(path)).ok()?; + let path = self.resolve_path(Path::new(path)); if let Some(ctx) = self .mod_cache .as_ref() @@ -1705,6 +1705,20 @@ impl Context { } } + // TODO: erg std + pub(crate) fn resolve_path(&self, path: &Path) -> PathBuf { + if let Ok(path) = self.cfg.input.resolve(path) { + path + } else if let Ok(path) = erg_pystd_path() + .join(format!("{}.d.er", path.display())) + .canonicalize() + { + path + } else { + PathBuf::from(format!(".{}", path.display())) + } + } + // FIXME: 現在の実装だとimportしたモジュールはどこからでも見れる pub(crate) fn get_mod(&self, name: &str) -> Option<&Context> { let t = self.get_var_info(name).map(|(_, vi)| vi.t.clone()).ok()?; @@ -1712,18 +1726,7 @@ impl Context { Type::Poly { name, mut params } if &name[..] == "Module" => { let path = option_enum_unwrap!(params.remove(0), TyParam::Value:(ValueObj::Str:(_)))?; - let path = Path::new(&path[..]); - // TODO: erg std - let path = if let Ok(path) = self.cfg.input.resolve(path) { - path - } else if let Ok(path) = erg_pystd_path() - .join(format!("{}.d.er", path.display())) - .canonicalize() - { - path - } else { - PathBuf::from(format!(".{}", path.display())) - }; + let path = self.resolve_path(Path::new(&path[..])); self.mod_cache .as_ref() .and_then(|cache| cache.ref_ctx(&path)) diff --git a/compiler/erg_compiler/context/instantiate.rs b/compiler/erg_compiler/context/instantiate.rs index 8ad73b62..03a854d7 100644 --- a/compiler/erg_compiler/context/instantiate.rs +++ b/compiler/erg_compiler/context/instantiate.rs @@ -797,11 +797,11 @@ impl Context { TypeSpec::PreDeclTy(predecl) => { Ok(self.instantiate_predecl_t(predecl, opt_decl_t, tmp_tv_ctx)?) } - TypeSpec::And(lhs, rhs) => Ok(self.union( + TypeSpec::And(lhs, rhs) => Ok(self.intersection( &self.instantiate_typespec(lhs, opt_decl_t, tmp_tv_ctx, mode)?, &self.instantiate_typespec(rhs, opt_decl_t, tmp_tv_ctx, mode)?, )), - TypeSpec::Or(lhs, rhs) => Ok(self.intersection( + TypeSpec::Or(lhs, rhs) => Ok(self.union( &self.instantiate_typespec(lhs, opt_decl_t, tmp_tv_ctx, mode)?, &self.instantiate_typespec(rhs, opt_decl_t, tmp_tv_ctx, mode)?, )), diff --git a/compiler/erg_compiler/lib/pystd/subprocess.d.er b/compiler/erg_compiler/lib/pystd/subprocess.d.er index fc758920..c59d0fe6 100644 --- a/compiler/erg_compiler/lib/pystd/subprocess.d.er +++ b/compiler/erg_compiler/lib/pystd/subprocess.d.er @@ -1 +1,14 @@ -.run!: (args: Str or [Str; _], shell := Bool) => NoneType +.CompletedProcess: ClassType +.CompletedProcess.args: Str or [Str; _] +.CompletedProcess.returncode: Int +.CompletedProcess.stdout: Bytes or NoneType +.CompletedProcess.stderr: Bytes or NoneType + +.run!: ( + args: Str or [Str; _], + stdin: File! or NoneType := NoneType, + stdout: File! or NoneType := NoneType, + stderr: File! or NoneType := NoneType, + capture_output := Bool, + shell := Bool, +) => .CompletedProcess diff --git a/compiler/erg_parser/error.rs b/compiler/erg_parser/error.rs index 6825d834..715eb074 100644 --- a/compiler/erg_parser/error.rs +++ b/compiler/erg_parser/error.rs @@ -33,6 +33,10 @@ impl LexError { Self(core) } + pub fn set_hint>(&mut self, hint: S) { + self.0.hint = Some(hint.into()); + } + pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { Self::new(ErrorCore::new( errno, diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 06349ca4..08887339 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -349,7 +349,7 @@ impl Parser { if self.cur_is(TokenKind::AtSign) { self.lpop(); let expr = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(Some(Decorator::new(expr))) @@ -436,7 +436,7 @@ impl Parser { Some(semi) if semi.is(Semi) => { self.lpop(); let len = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; return Ok(ArrayInner::WithLength(elems.remove_pos(0), len)); @@ -493,7 +493,7 @@ impl Parser { match self.peek() { Some(_) => { let expr = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(PosArg::new(expr)) @@ -667,7 +667,6 @@ impl Parser { } if self.nth_is(1, Walrus) { let acc = self.try_reduce_acc_lhs().map_err(|_| self.stack_dec())?; - // TODO: type specification debug_power_assert!(self.cur_is(Walrus)); self.skip(); let kw = if let Accessor::Ident(n) = acc { @@ -680,21 +679,52 @@ impl Parser { return Err(()); }; let expr = self - .try_reduce_expr(false, in_type_args, false) + .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(PosOrKwArg::Kw(KwArg::new(kw, None, expr))) } else { let expr = self - .try_reduce_expr(false, in_type_args, false) + .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(PosOrKwArg::Pos(PosArg::new(expr))) + if self.cur_is(Walrus) { + self.skip(); + let (kw, t_spec) = match expr { + Expr::Accessor(Accessor::Ident(n)) => (n.name.into_token(), None), + Expr::TypeAsc(tasc) => { + if let Expr::Accessor(Accessor::Ident(n)) = *tasc.expr { + let t_spec = TypeSpecWithOp::new(tasc.op, tasc.t_spec); + (n.name.into_token(), Some(t_spec)) + } else { + self.next_expr(); + self.level -= 1; + let err = ParseError::simple_syntax_error(0, tasc.loc()); + self.errs.push(err); + return Err(()); + } + } + _ => { + self.next_expr(); + self.level -= 1; + let err = ParseError::simple_syntax_error(0, expr.loc()); + self.errs.push(err); + return Err(()); + } + }; + let expr = self + .try_reduce_expr(false, in_type_args, false, false) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(PosOrKwArg::Kw(KwArg::new(kw, t_spec, expr))) + } else { + self.level -= 1; + Ok(PosOrKwArg::Pos(PosArg::new(expr))) + } } } Some(_) => { let expr = self - .try_reduce_expr(false, in_type_args, false) + .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(PosOrKwArg::Pos(PosArg::new(expr))) @@ -731,7 +761,7 @@ impl Parser { None };*/ let expr = self - .try_reduce_expr(false, in_type_args, false) + .try_reduce_expr(false, in_type_args, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(KwArg::new(keyword, None, expr)) @@ -834,7 +864,7 @@ impl Parser { Ok(Lambda::new(sig, op, body, self.counter)) } else { let expr = self - .try_reduce_expr(true, false, false) + .try_reduce_expr(true, false, false, false) .map_err(|_| self.stack_dec())?; let block = Block::new(vec![expr]); self.level -= 1; @@ -901,7 +931,7 @@ impl Parser { let op = self.lpop(); let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; let t_spec = Self::expr_to_type_spec(t_spec).map_err(|e| self.errs.push(e))?; let expr = Expr::TypeAsc(TypeAscription::new(lhs, op, t_spec)); @@ -1055,7 +1085,7 @@ impl Parser { }; self.skip(); let index = self - .try_reduce_expr(false, false, in_brace) + .try_reduce_expr(false, false, in_brace, false) .map_err(|_| self.stack_dec())?; let r_sqbr = self.lpop(); if !r_sqbr.is(RSqBr) { @@ -1071,7 +1101,7 @@ impl Parser { Some(t) if t.is(Comma) && winding => { let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let tup = self - .try_reduce_tuple(first_elem) + .try_reduce_tuple(first_elem, false) .map_err(|_| self.stack_dec())?; stack.push(ExprOrOp::Expr(Expr::Tuple(tup))); } @@ -1131,6 +1161,7 @@ impl Parser { winding: bool, in_type_args: bool, in_brace: bool, + line_break: bool, ) -> ParseResult { debug_call_info!(self); let mut stack = Vec::::new(); @@ -1167,7 +1198,7 @@ impl Parser { let op = self.lpop(); let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec = self - .try_reduce_expr(false, in_type_args, in_brace) + .try_reduce_expr(false, in_type_args, in_brace, false) .map_err(|_| self.stack_dec())?; let t_spec = Self::expr_to_type_spec(t_spec).map_err(|e| self.errs.push(e))?; let expr = Expr::TypeAsc(TypeAscription::new(lhs, op, t_spec)); @@ -1237,7 +1268,7 @@ impl Parser { Some(t) if t.is(Comma) && winding => { let first_elem = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let tup = self - .try_reduce_tuple(first_elem) + .try_reduce_tuple(first_elem, line_break) .map_err(|_| self.stack_dec())?; stack.push(ExprOrOp::Expr(Expr::Tuple(tup))); } @@ -1348,6 +1379,15 @@ impl Parser { } Some(t) if t.is(LParen) => { let lparen = self.lpop(); + while self.cur_is(Newline) { + self.skip(); + } + let line_break = if self.cur_is(Indent) { + self.skip(); + true + } else { + false + }; if self.cur_is(RParen) { let rparen = self.lpop(); let args = Args::new(vec![], vec![], Some((lparen, rparen))); @@ -1356,8 +1396,14 @@ impl Parser { return Ok(Expr::Tuple(unit)); } let mut expr = self - .try_reduce_expr(true, false, false) + .try_reduce_expr(true, false, false, line_break) .map_err(|_| self.stack_dec())?; + while self.cur_is(Newline) { + self.skip(); + } + if self.cur_is(Dedent) { + self.skip(); + } let rparen = self.lpop(); if let Expr::Tuple(Tuple::Normal(tup)) = &mut expr { tup.elems.paren = Some((lparen, rparen)); @@ -1436,7 +1482,7 @@ impl Parser { Some(t) if t.is(LSqBr) && obj.col_end() == t.col_begin() => { let _l_sqbr = self.lpop(); let index = self - .try_reduce_expr(true, false, false) + .try_reduce_expr(true, false, false, false) .map_err(|_| self.stack_dec())?; let r_sqbr = if self.cur_is(RSqBr) { self.lpop() @@ -1556,7 +1602,7 @@ impl Parser { debug_call_info!(self); let op = self.lpop(); let expr = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; self.level -= 1; Ok(UnaryOp::new(op, expr)) @@ -1856,7 +1902,7 @@ impl Parser { return Ok(dict); } let key = self - .try_reduce_expr(false, false, true) + .try_reduce_expr(false, false, true, false) .map_err(|_| self.stack_dec())?; if self.cur_is(Colon) { self.skip(); @@ -1889,7 +1935,7 @@ impl Parser { if self.cur_is(Semi) { self.skip(); let len = self - .try_reduce_expr(false, false, false) + .try_reduce_expr(false, false, false, false) .map_err(|_| self.stack_dec())?; let r_brace = self.lpop(); return Ok(Set::WithLength(SetWithLength::new( @@ -1947,19 +1993,22 @@ impl Parser { Err(()) } - fn try_reduce_tuple(&mut self, first_elem: Expr) -> ParseResult { + fn try_reduce_tuple(&mut self, first_elem: Expr, line_break: bool) -> ParseResult { debug_call_info!(self); let mut args = Args::new(vec![PosArg::new(first_elem)], vec![], None); loop { match self.peek() { Some(t) if t.is(Comma) => { self.skip(); + while self.cur_is(Newline) && line_break { + self.skip(); + } if self.cur_is(Comma) { self.level -= 1; let err = self.skip_and_throw_syntax_err(caused_by!()); self.errs.push(err); return Err(()); - } else if self.cur_is(RParen) { + } else if self.cur_is(Dedent) || self.cur_is(RParen) { break; } match self.try_reduce_arg(false).map_err(|_| self.stack_dec())? { @@ -2916,6 +2965,13 @@ impl Parser { Err(err) } } + Expr::Lit(lit) => { + let mut err = ParseError::simple_syntax_error(line!() as usize, lit.loc()); + if lit.is(TokenKind::NoneLit) { + err.set_hint("you mean: `NoneType`?"); + } + Err(err) + } other => { let err = ParseError::simple_syntax_error(line!() as usize, other.loc()); Err(err) diff --git a/examples/pyimport.er b/examples/pyimport.er index a2c4966e..83033d8e 100644 --- a/examples/pyimport.er +++ b/examples/pyimport.er @@ -1,5 +1,7 @@ math = pyimport "math" sys = pyimport "sys" +sub = pyimport "subprocess" print! math.pi +discard sub.run! ["echo", "hello"], shell := True sys.exit 111 diff --git a/tests/test.rs b/tests/test.rs index 4254e143..47116197 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2,13 +2,15 @@ use std::path::PathBuf; use erg_common::config::ErgConfig; use erg_common::error::MultiErrorDisplay; -use erg_common::traits::Runnable; +use erg_common::traits::{Runnable, Stream}; + +use erg_compiler::error::CompileErrors; use erg::dummy::DummyVM; #[test] fn exec_addition() -> Result<(), ()> { - expect_failure("tests/addition.er") + expect_failure("tests/addition.er", 1) } #[test] @@ -63,7 +65,7 @@ fn exec_infer_trait() -> Result<(), ()> { #[test] fn exec_move_check() -> Result<(), ()> { - expect_failure("examples/move_check.er") + expect_failure("examples/move_check.er", 1) } #[test] @@ -89,17 +91,17 @@ fn exec_record() -> Result<(), ()> { #[test] fn exec_set() -> Result<(), ()> { - expect_failure("examples/set.er") + expect_failure("examples/set.er", 1) } #[test] fn exec_side_effect() -> Result<(), ()> { - expect_failure("examples/side_effect.er") + expect_failure("examples/side_effect.er", 4) } #[test] fn exec_subtyping() -> Result<(), ()> { - expect_failure("tests/subtyping.er") + expect_failure("tests/subtyping.er", 1) } #[test] @@ -128,9 +130,7 @@ fn exec_with() -> Result<(), ()> { } fn expect_success(file_path: &'static str) -> Result<(), ()> { - let cfg = ErgConfig::with_main_path(PathBuf::from(file_path)); - let mut vm = DummyVM::new(cfg); - match vm.exec() { + match exec_vm(file_path) { Ok(0) => Ok(()), Ok(i) => { println!("err: end with {i}"); @@ -144,9 +144,7 @@ fn expect_success(file_path: &'static str) -> Result<(), ()> { } fn expect_end_with(file_path: &'static str, code: i32) -> Result<(), ()> { - let cfg = ErgConfig::with_main_path(PathBuf::from(file_path)); - let mut vm = DummyVM::new(cfg); - match vm.exec() { + match exec_vm(file_path) { Ok(0) => Err(()), Ok(i) => { if i == code { @@ -163,15 +161,41 @@ fn expect_end_with(file_path: &'static str, code: i32) -> Result<(), ()> { } } -fn expect_failure(file_path: &'static str) -> Result<(), ()> { - let cfg = ErgConfig::with_main_path(PathBuf::from(file_path)); - let mut vm = DummyVM::new(cfg); - match vm.exec() { +fn expect_failure(file_path: &'static str, errs_len: usize) -> Result<(), ()> { + match exec_vm(file_path) { Ok(0) => Err(()), Ok(_) => Ok(()), Err(errs) => { errs.fmt_all_stderr(); - Ok(()) + if errs.len() == errs_len { + Ok(()) + } else { + println!("err: error length is not {errs_len} but {}", errs.len()); + Err(()) + } } } } + +fn _exec_vm(file_path: &'static str) -> Result { + let cfg = ErgConfig::with_main_path(PathBuf::from(file_path)); + let mut vm = DummyVM::new(cfg); + vm.exec() +} + +#[cfg(target_os = "windows")] +fn exec_vm(file_path: &'static str) -> Result { + const STACK_SIZE: usize = 4 * 1024 * 1024; + + let child = std::thread::Builder::new() + .stack_size(STACK_SIZE) + .spawn(move || _exec_vm(file_path)) + .unwrap(); + // Wait for thread to join + child.join().unwrap() +} + +#[cfg(not(target_os = "windows"))] +fn exec_vm(file_path: &'static str) -> Result { + _exec_vm(file_path) +}