This commit is contained in:
Shunsuke Shibayama 2022-10-18 17:17:56 +09:00
parent afcf21787d
commit ae15f95191
7 changed files with 159 additions and 57 deletions

View file

@ -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,16 +1705,9 @@ impl Context {
}
}
// 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()?;
match t {
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) {
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()))
@ -1723,7 +1716,17 @@ impl Context {
path
} else {
PathBuf::from(format!("<builtins>.{}", 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()?;
match t {
Type::Poly { name, mut params } if &name[..] == "Module" => {
let path =
option_enum_unwrap!(params.remove(0), TyParam::Value:(ValueObj::Str:(_)))?;
let path = self.resolve_path(Path::new(&path[..]));
self.mod_cache
.as_ref()
.and_then(|cache| cache.ref_ctx(&path))

View file

@ -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)?,
)),

View file

@ -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

View file

@ -33,6 +33,10 @@ impl LexError {
Self(core)
}
pub fn set_hint<S: Into<AtomicStr>>(&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,

View file

@ -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())?;
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<Expr> {
debug_call_info!(self);
let mut stack = Vec::<ExprOrOp>::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<Tuple> {
fn try_reduce_tuple(&mut self, first_elem: Expr, line_break: bool) -> ParseResult<Tuple> {
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)

View file

@ -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

View file

@ -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();
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<i32, CompileErrors> {
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<i32, CompileErrors> {
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<i32, CompileErrors> {
_exec_vm(file_path)
}