diff --git a/Cargo.lock b/Cargo.lock index a053c946..869aa653 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ [[package]] name = "erg" -version = "0.4.1" +version = "0.4.6" dependencies = [ "erg_common", "erg_compiler", @@ -25,14 +25,14 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.4.1" +version = "0.4.6" dependencies = [ "atty", ] [[package]] name = "erg_compiler" -version = "0.4.1" +version = "0.4.6" dependencies = [ "erg_common", "erg_parser", @@ -41,14 +41,14 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.4.1" +version = "0.4.6" dependencies = [ "erg_common", ] [[package]] name = "erg_type" -version = "0.4.1" +version = "0.4.6" dependencies = [ "erg_common", ] diff --git a/Cargo.toml b/Cargo.toml index dfa0bd1f..dad50dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erg" -version = "0.4.1" +version = "0.4.6" description = "The Erg programming language" authors = ["Shunsuke Shibayama "] license = "MIT OR Apache-2.0" @@ -46,10 +46,10 @@ traditional_chinese = [ ] [dependencies] -erg_common = { version = "0.4.1", path = "./compiler/erg_common" } -erg_parser = { version = "0.4.1", path = "./compiler/erg_parser" } -erg_compiler = { version = "0.4.1", path = "./compiler/erg_compiler" } -erg_type = { version = "0.4.1", path = "./compiler/erg_type" } +erg_common = { version = "0.4.6", path = "./compiler/erg_common" } +erg_parser = { version = "0.4.6", path = "./compiler/erg_parser" } +erg_compiler = { version = "0.4.6", path = "./compiler/erg_compiler" } +erg_type = { version = "0.4.6", path = "./compiler/erg_type" } # [workspace] # member = ["cm", "dyne"] diff --git a/compiler/erg_common/Cargo.toml b/compiler/erg_common/Cargo.toml index 487dc73b..d732057e 100644 --- a/compiler/erg_common/Cargo.toml +++ b/compiler/erg_common/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "erg_common" -version = "0.4.1" +version = "0.4.6" description = "A common components library of Erg" -authors = ["Shunsuke Shibayama "] +authors = ["erg-lang team "] license = "MIT OR Apache-2.0" edition = "2021" repository = "https://github.com/erg-lang/erg/tree/main/src/erg_common" diff --git a/compiler/erg_common/astr.rs b/compiler/erg_common/astr.rs new file mode 100644 index 00000000..239b0430 --- /dev/null +++ b/compiler/erg_common/astr.rs @@ -0,0 +1,166 @@ +use std::borrow::Borrow; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::{Add, Deref}; + +use crate::Str; + +pub type ArcStr = std::sync::Arc; + +/// Used to hold an immutable string. +/// +/// It can construct as a const (by AtomicStr::ever). +#[derive(Debug, Clone, Eq)] +pub enum AtomicStr { + Arc(ArcStr), + Static(&'static str), +} + +// unsafe impl Sync for AtomicStr {} + +impl PartialEq for AtomicStr { + #[inline] + fn eq(&self, other: &AtomicStr) -> bool { + self[..] == other[..] + } +} + +impl Add<&str> for AtomicStr { + type Output = AtomicStr; + #[inline] + fn add(self, other: &str) -> AtomicStr { + AtomicStr::from(&format!("{self}{other}")) + } +} + +impl Hash for AtomicStr { + fn hash(&self, state: &mut H) { + match self { + AtomicStr::Arc(s) => (s[..]).hash(state), + AtomicStr::Static(s) => s.hash(state), + } + } +} + +impl fmt::Display for AtomicStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + AtomicStr::Arc(s) => write!(f, "{s}"), + AtomicStr::Static(s) => write!(f, "{s}"), + } + } +} + +// &'static str -> &strになってしまわないように +// あえて`impl> From for AtomicStr { ... }`はしない +impl From<&'static str> for AtomicStr { + #[inline] + fn from(s: &'static str) -> Self { + AtomicStr::ever(s) + } +} + +impl From<&String> for AtomicStr { + #[inline] + fn from(s: &String) -> Self { + AtomicStr::Arc((s[..]).into()) + } +} + +impl From for AtomicStr { + #[inline] + fn from(s: String) -> Self { + AtomicStr::Arc((s[..]).into()) + } +} + +impl From<&ArcStr> for AtomicStr { + #[inline] + fn from(s: &ArcStr) -> Self { + AtomicStr::Arc(s.clone()) + } +} + +impl From for AtomicStr { + #[inline] + fn from(s: ArcStr) -> Self { + AtomicStr::Arc(s) + } +} + +impl From<&AtomicStr> for AtomicStr { + #[inline] + fn from(s: &AtomicStr) -> Self { + match s { + AtomicStr::Arc(s) => AtomicStr::Arc(s.clone()), + AtomicStr::Static(s) => AtomicStr::Static(s), + } + } +} + +impl From for AtomicStr { + #[inline] + fn from(s: Str) -> Self { + match s { + Str::Rc(s) => AtomicStr::Arc((&s[..]).into()), + Str::Static(s) => AtomicStr::Static(s), + } + } +} + +impl From<&Str> for AtomicStr { + #[inline] + fn from(s: &Str) -> Self { + match s { + Str::Rc(s) => AtomicStr::Arc((&s[..]).into()), + Str::Static(s) => AtomicStr::Static(s), + } + } +} + +impl Deref for AtomicStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.borrow() + } +} + +impl Borrow for AtomicStr { + #[inline] + fn borrow(&self) -> &str { + match self { + AtomicStr::Arc(s) => s.borrow(), + AtomicStr::Static(s) => s, + } + } +} + +impl AsRef for AtomicStr { + fn as_ref(&self) -> &str { + self.borrow() + } +} + +impl AtomicStr { + pub const fn ever(s: &'static str) -> Self { + AtomicStr::Static(s) + } + + pub fn arc(s: &str) -> Self { + AtomicStr::Arc(s.into()) + } + + pub fn into_rc(self) -> ArcStr { + match self { + AtomicStr::Arc(s) => s, + AtomicStr::Static(s) => ArcStr::from(s), + } + } + + pub fn is_uppercase(&self) -> bool { + self.chars() + .next() + .map(|c| c.is_uppercase()) + .unwrap_or(false) + } +} diff --git a/compiler/erg_common/config.rs b/compiler/erg_common/config.rs index 5c362cfb..ebe08279 100644 --- a/compiler/erg_common/config.rs +++ b/compiler/erg_common/config.rs @@ -7,7 +7,6 @@ use std::io::{stdin, BufRead, BufReader, Read}; use std::process; use crate::stdin::GLOBAL_STDIN; -use crate::Str; use crate::{power_assert, read_file}; pub const SEMVER: &str = env!("CARGO_PKG_VERSION"); @@ -19,12 +18,12 @@ pub const BUILD_DATE: &str = env!("BUILD_DATE"); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Input { /// filename - File(Str), + File(String), REPL, /// same content as cfg.command - Pipe(Str), + Pipe(String), /// from command option | eval - Str(Str), + Str(String), Dummy, } @@ -52,7 +51,7 @@ impl Input { } } - pub fn read(&self) -> Str { + pub fn read(&self) -> String { match self { Self::File(filename) => { let file = match File::open(&filename[..]) { @@ -63,15 +62,14 @@ impl Input { process::exit(code); } }; - let src = match read_file(file) { + match read_file(file) { Ok(s) => s, Err(e) => { let code = e.raw_os_error().unwrap_or(1); println!("cannot read '{filename}': [Errno {code}] {e}"); process::exit(code); } - }; - Str::from(src) + } } Self::Pipe(s) | Self::Str(s) => s.clone(), Self::REPL => GLOBAL_STDIN.read(), @@ -79,7 +77,7 @@ impl Input { } } - pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { + pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { power_assert!(ln_begin, >=, 1); match self { Self::File(filename) => match File::open(&filename[..]) { @@ -87,7 +85,7 @@ impl Input { let mut codes = vec![]; let mut lines = BufReader::new(file).lines().skip(ln_begin - 1); for _ in ln_begin..=ln_end { - codes.push(Str::from(lines.next().unwrap().unwrap())); + codes.push(lines.next().unwrap().unwrap()); } codes } @@ -96,18 +94,18 @@ impl Input { Self::Pipe(s) | Self::Str(s) => s.split('\n').collect::>() [ln_begin - 1..=ln_end - 1] .iter() - .map(|s| Str::rc(*s)) + .map(|s| s.to_string()) .collect(), Self::REPL => GLOBAL_STDIN.reread_lines(ln_begin, ln_end), Self::Dummy => panic!("cannot read lines from a dummy file"), } } - pub fn reread(&self) -> Str { + pub fn reread(&self) -> String { match self { Self::File(_filename) => todo!(), Self::Pipe(s) | Self::Str(s) => s.clone(), - Self::REPL => Str::from(GLOBAL_STDIN.reread().trim_end().to_owned()), + Self::REPL => GLOBAL_STDIN.reread().trim_end().to_owned(), Self::Dummy => panic!("cannot read from a dummy file"), } } @@ -126,6 +124,7 @@ pub struct ErgConfig { pub dump_as_pyc: bool, pub python_ver: Option, pub py_server_timeout: u64, + pub quiet_startup: bool, pub input: Input, pub module: &'static str, /// verbosity level for system messages. @@ -142,7 +141,7 @@ impl Default for ErgConfig { let input = if is_stdin_piped { let mut buffer = String::new(); stdin().read_to_string(&mut buffer).unwrap(); - Input::Pipe(Str::from(buffer)) + Input::Pipe(buffer) } else { Input::REPL }; @@ -152,6 +151,7 @@ impl Default for ErgConfig { dump_as_pyc: false, python_ver: None, py_server_timeout: 10, + quiet_startup: false, input, module: "", verbose: 2, @@ -174,7 +174,7 @@ impl ErgConfig { while let Some(arg) = args.next() { match &arg[..] { "-c" => { - cfg.input = Input::Str(Str::from(args.next().unwrap())); + cfg.input = Input::Str(args.next().unwrap()); } "--dump-as-pyc" => { cfg.dump_as_pyc = true; @@ -212,6 +212,9 @@ impl ErgConfig { "--py-server-timeout" => { cfg.py_server_timeout = args.next().unwrap().parse::().unwrap(); } + "--quiet-startup" => { + cfg.quiet_startup = true; + } "--verbose" => { cfg.verbose = args.next().unwrap().parse::().unwrap(); } @@ -223,7 +226,7 @@ impl ErgConfig { panic!("invalid option: {other}"); } _ => { - cfg.input = Input::File(Str::from(arg)); + cfg.input = Input::File(arg); break; } } diff --git a/compiler/erg_common/error.rs b/compiler/erg_common/error.rs index ad832bee..e610735d 100644 --- a/compiler/erg_common/error.rs +++ b/compiler/erg_common/error.rs @@ -6,10 +6,10 @@ use std::fmt; use std::fmt::Write as _; use std::io::{stderr, BufWriter, Write as _}; +use crate::astr::AtomicStr; use crate::color::*; use crate::config::Input; use crate::traits::{Locational, Stream}; -use crate::Str; use crate::{fmt_option, impl_display_from_debug, switch_lang}; /// ErrorKindと言っているが、ErrorだけでなくWarning, Exceptionも含まれる @@ -306,17 +306,17 @@ pub struct ErrorCore { pub errno: usize, pub kind: ErrorKind, pub loc: Location, - pub desc: Str, - pub hint: Option, + pub desc: AtomicStr, + pub hint: Option, } impl ErrorCore { - pub fn new>( + pub fn new>( errno: usize, kind: ErrorKind, loc: Location, desc: S, - hint: Option, + hint: Option, ) -> Self { Self { errno, @@ -423,27 +423,31 @@ pub trait ErrorDisplay { fn ref_inner(&self) -> Option<&Self>; fn write_to_stderr(&self) { - let mut writer = BufWriter::new(stderr()); - writer - .write_all( - format!( - "{}{}{}: {}{}\n", - self.format_header(), - self.format_code_and_pointer(), - self.core().kind, - self.core().desc, - fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint), - ) - .as_bytes(), - ) - .unwrap(); + let mut stderr = stderr(); + self.write_to(&mut stderr) + } + + fn write_to(&self, w: &mut W) { + let mut writer = BufWriter::new(w); + writer.write_all(self.show().as_bytes()).unwrap(); writer.flush().unwrap(); if let Some(inner) = self.ref_inner() { inner.write_to_stderr() } } - /// fmt::Display実装用 + fn show(&self) -> String { + format!( + "{}{}{}: {}{}\n", + self.format_header(), + self.format_code_and_pointer(), + self.core().kind, + self.core().desc, + fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), self.core().hint) + ) + } + + /// for fmt::Display fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!( f, diff --git a/compiler/erg_common/lib.rs b/compiler/erg_common/lib.rs index ed50f82f..9e6520c1 100644 --- a/compiler/erg_common/lib.rs +++ b/compiler/erg_common/lib.rs @@ -1,6 +1,7 @@ //! provides utilities for parser, compiler, and vm crate. use std::fmt; +pub mod astr; pub mod cache; pub mod color; pub mod config; diff --git a/compiler/erg_common/stdin.rs b/compiler/erg_common/stdin.rs index 1e1b02a4..06157586 100644 --- a/compiler/erg_common/stdin.rs +++ b/compiler/erg_common/stdin.rs @@ -2,29 +2,27 @@ use std::cell::RefCell; use std::io::{stdin, BufRead, BufReader}; use std::thread::LocalKey; -use crate::Str; - pub struct StdinReader { pub lineno: usize, - buf: Vec, + buf: Vec, } impl StdinReader { - pub fn read(&mut self) -> Str { + pub fn read(&mut self) -> String { let mut buf = "".to_string(); let stdin = stdin(); let mut reader = BufReader::new(stdin.lock()); reader.read_line(&mut buf).unwrap(); self.lineno += 1; - self.buf.push(buf.into()); + self.buf.push(buf); self.buf.last().unwrap().clone() } - pub fn reread(&self) -> Str { + pub fn reread(&self) -> String { self.buf.last().unwrap().clone() } - pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { + pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { self.buf[ln_begin - 1..=ln_end - 1].to_vec() } } @@ -39,15 +37,15 @@ pub struct GlobalStdin(LocalKey>); pub static GLOBAL_STDIN: GlobalStdin = GlobalStdin(READER); impl GlobalStdin { - pub fn read(&'static self) -> Str { + pub fn read(&'static self) -> String { self.0.with(|s| s.borrow_mut().read()) } - pub fn reread(&'static self) -> Str { + pub fn reread(&'static self) -> String { self.0.with(|s| s.borrow().reread()) } - pub fn reread_lines(&'static self, ln_begin: usize, ln_end: usize) -> Vec { + pub fn reread_lines(&'static self, ln_begin: usize, ln_end: usize) -> Vec { self.0 .with(|s| s.borrow_mut().reread_lines(ln_begin, ln_end)) } diff --git a/compiler/erg_common/traits.rs b/compiler/erg_common/traits.rs index 1af4cf37..5ff5d6b1 100644 --- a/compiler/erg_common/traits.rs +++ b/compiler/erg_common/traits.rs @@ -10,7 +10,6 @@ use std::vec::IntoIter; use crate::config::{ErgConfig, Input, BUILD_DATE, GIT_HASH_SHORT, SEMVER}; use crate::error::{ErrorDisplay, ErrorKind, Location, MultiErrorDisplay}; -use crate::Str; use crate::{addr_eq, chomp, log, switch_unreachable}; pub trait Stream: Sized { @@ -312,6 +311,12 @@ fn expect_block(src: &str) -> bool { src.ends_with(&['.', '=', ':']) || src.ends_with("->") || src.ends_with("=>") } +// In the REPL, it is invalid for these symbols to be at the beginning of a line +fn expect_invalid_block(src: &str) -> bool { + let src = src.trim_start(); + src.starts_with(&['.', '=', ':']) || src.starts_with("->") +} + /// This trait implements REPL (Read-Eval-Print-Loop) automatically /// The `exec` method is called for file input, etc. pub trait Runnable: Sized { @@ -328,7 +333,7 @@ pub trait Runnable: Sized { } fn finish(&mut self); // called when the :exit command is received. fn clear(&mut self); - fn eval(&mut self, src: Str) -> Result; + fn eval(&mut self, src: String) -> Result; fn exec(&mut self) -> Result<(), Self::Errs>; fn ps1(&self) -> String { @@ -344,16 +349,19 @@ pub trait Runnable: Sized { } fn run(cfg: ErgConfig) { + let quiet_startup = cfg.quiet_startup; let mut instance = Self::new(cfg); let res = match instance.input() { Input::File(_) | Input::Pipe(_) | Input::Str(_) => instance.exec(), Input::REPL => { let output = stdout(); let mut output = BufWriter::new(output.lock()); - log!(info_f output, "The REPL has started.\n"); - output - .write_all(instance.start_message().as_bytes()) - .unwrap(); + if !quiet_startup { + log!(info_f output, "The REPL has started.\n"); + output + .write_all(instance.start_message().as_bytes()) + .unwrap(); + } output.write_all(instance.ps1().as_bytes()).unwrap(); output.flush().unwrap(); let mut lines = String::new(); @@ -381,13 +389,15 @@ pub trait Runnable: Sized { &line[..] }; lines.push_str(line); - if expect_block(line) || line.starts_with(' ') { + if expect_block(line) && !expect_invalid_block(line) + || line.starts_with(' ') && lines.contains('\n') + { lines += "\n"; output.write_all(instance.ps2().as_bytes()).unwrap(); output.flush().unwrap(); continue; } - match instance.eval(mem::take(&mut lines).into()) { + match instance.eval(mem::take(&mut lines)) { Ok(out) => { output.write_all((out + "\n").as_bytes()).unwrap(); output.flush().unwrap(); diff --git a/compiler/erg_compiler/Cargo.toml b/compiler/erg_compiler/Cargo.toml index 5ef8ebeb..073ce53b 100644 --- a/compiler/erg_compiler/Cargo.toml +++ b/compiler/erg_compiler/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "erg_compiler" -version = "0.4.1" +version = "0.4.6" description = "Centimetre: the Erg compiler" -authors = ["Shunsuke Shibayama "] +authors = ["erg-lang team "] license = "MIT OR Apache-2.0" edition = "2021" repository = "https://github.com/erg-lang/erg/tree/main/src/compiler/erg_compiler" @@ -17,9 +17,9 @@ simplified_chinese = [ "erg_common/simplified_chinese", "erg_parser/simplified_c traditional_chinese = [ "erg_common/traditional_chinese", "erg_parser/traditional_chinese", "erg_type/traditional_chinese" ] [dependencies] -erg_common = { version = "0.4.1", path = "../erg_common" } -erg_parser = { version = "0.4.1", path = "../erg_parser" } -erg_type = { version = "0.4.1", path = "../erg_type" } +erg_common = { version = "0.4.6", path = "../erg_common" } +erg_parser = { version = "0.4.6", path = "../erg_parser" } +erg_type = { version = "0.4.6", path = "../erg_type" } [lib] path = "lib.rs" diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 6567e05f..53825d3e 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -4,6 +4,7 @@ use std::fmt; use std::process; +use erg_common::astr::AtomicStr; use erg_common::cache::CacheSet; use erg_common::config::{ErgConfig, Input}; use erg_common::error::{Location, MultiErrorDisplay}; @@ -1220,7 +1221,7 @@ impl CodeGenerator { self.cfg.input.clone(), unary.op.loc(), "", - unary.op.content.clone(), + AtomicStr::from(unary.op.content), )); NOT_IMPLEMENTED } @@ -1267,7 +1268,7 @@ impl CodeGenerator { self.cfg.input.clone(), bin.op.loc(), "", - bin.op.content.clone(), + AtomicStr::from(bin.op.content), )); NOT_IMPLEMENTED } diff --git a/compiler/erg_compiler/compile.rs b/compiler/erg_compiler/compile.rs index 48edec1a..86d22b1f 100644 --- a/compiler/erg_compiler/compile.rs +++ b/compiler/erg_compiler/compile.rs @@ -7,7 +7,6 @@ use erg_common::config::{ErgConfig, Input}; use erg_common::error::MultiErrorDisplay; use erg_common::log; use erg_common::traits::{Runnable, Stream}; -use erg_common::Str; use erg_type::codeobj::CodeObj; use erg_parser::ParserRunner; @@ -134,7 +133,7 @@ impl Runnable for Compiler { self.compile_and_dump_as_pyc(src, path, "exec") } - fn eval(&mut self, src: Str) -> Result { + fn eval(&mut self, src: String) -> Result { let codeobj = self.compile(src, "eval")?; Ok(codeobj.code_info()) } @@ -150,7 +149,7 @@ impl Compiler { pub fn compile_and_dump_as_pyc>( &mut self, - src: Str, + src: String, path: P, mode: &str, ) -> Result<(), CompileErrors> { @@ -160,7 +159,7 @@ impl Compiler { Ok(()) } - pub fn compile(&mut self, src: Str, mode: &str) -> Result { + pub fn compile(&mut self, src: String, mode: &str) -> Result { log!(info "the compiling process has started."); let mut cfg = self.cfg.copy(); cfg.input = Input::Str(src); diff --git a/compiler/erg_compiler/context/compare.rs b/compiler/erg_compiler/context/compare.rs index 38903ca1..40849764 100644 --- a/compiler/erg_compiler/context/compare.rs +++ b/compiler/erg_compiler/context/compare.rs @@ -780,23 +780,16 @@ impl Context { } fn union_refinement(&self, lhs: &RefinementType, rhs: &RefinementType) -> RefinementType { - if let Some(max) = self.max(&lhs.t, &rhs.t) { - let name = lhs.var.clone(); - let rhs_preds = rhs - .preds - .iter() - .map(|p| p.clone().change_subject_name(name.clone())) - .collect(); - // FIXME: predの包含関係も考慮する - RefinementType::new( - lhs.var.clone(), - max.clone(), - lhs.preds.clone().concat(rhs_preds), - ) - } else { - log!(info "{lhs}\n{rhs}"); - todo!() - } + // TODO: warn if lhs.t !:> rhs.t && rhs.t !:> lhs.t + let union = self.union(&lhs.t, &rhs.t); + let name = lhs.var.clone(); + let rhs_preds = rhs + .preds + .iter() + .map(|p| p.clone().change_subject_name(name.clone())) + .collect(); + // FIXME: predの包含関係も考慮する + RefinementType::new(lhs.var.clone(), union, lhs.preds.clone().concat(rhs_preds)) } /// returns intersection of two types (A and B) diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 886e3557..5903c5b7 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -1,6 +1,7 @@ use std::mem; use erg_common::dict::Dict; +use erg_common::error::Location; use erg_common::rccell::RcCell; use erg_common::set::Set; use erg_common::traits::{Locational, Stream}; @@ -496,21 +497,28 @@ impl Context { } } - pub(crate) fn eval_t_params(&self, substituted: Type, level: usize) -> EvalResult { + pub(crate) fn eval_t_params( + &self, + substituted: Type, + level: usize, + t_loc: Location, + ) -> EvalResult { match substituted { - Type::FreeVar(fv) if fv.is_linked() => self.eval_t_params(fv.crack().clone(), level), + Type::FreeVar(fv) if fv.is_linked() => { + self.eval_t_params(fv.crack().clone(), level, t_loc) + } Type::Subr(mut subr) => { for pt in subr.non_default_params.iter_mut() { - *pt.typ_mut() = self.eval_t_params(mem::take(pt.typ_mut()), level)?; + *pt.typ_mut() = self.eval_t_params(mem::take(pt.typ_mut()), level, t_loc)?; } if let Some(var_args) = subr.var_params.as_mut() { *var_args.typ_mut() = - self.eval_t_params(mem::take(var_args.typ_mut()), level)?; + self.eval_t_params(mem::take(var_args.typ_mut()), level, t_loc)?; } for pt in subr.default_params.iter_mut() { - *pt.typ_mut() = self.eval_t_params(mem::take(pt.typ_mut()), level)?; + *pt.typ_mut() = self.eval_t_params(mem::take(pt.typ_mut()), level, t_loc)?; } - let return_t = self.eval_t_params(*subr.return_t, level)?; + let return_t = self.eval_t_params(*subr.return_t, level, t_loc)?; Ok(subr_t( subr.kind, subr.non_default_params, @@ -533,7 +541,7 @@ impl Context { // All type variables will be dereferenced or fail. let (sub, opt_sup) = match *lhs.clone() { Type::FreeVar(fv) if fv.is_linked() => { - return self.eval_t_params(mono_proj(fv.crack().clone(), rhs), level) + return self.eval_t_params(mono_proj(fv.crack().clone(), rhs), level, t_loc) } Type::FreeVar(fv) if fv.is_unbound() => { let (sub, sup) = fv.get_bound_types().unwrap(); @@ -553,7 +561,7 @@ impl Context { if let ValueObj::Type(quant_t) = obj { let subst_ctx = SubstContext::new(&sub, ty_ctx); let t = subst_ctx.substitute(quant_t.typ().clone(), self)?; - let t = self.eval_t_params(t, level)?; + let t = self.eval_t_params(t, level, t_loc)?; return Ok(t); } else { todo!() @@ -577,7 +585,7 @@ impl Context { if let ValueObj::Type(quant_t) = obj { let subst_ctx = SubstContext::new(&lhs, ty_ctx); let t = subst_ctx.substitute(quant_t.typ().clone(), self)?; - let t = self.eval_t_params(t, level)?; + let t = self.eval_t_params(t, level, t_loc)?; return Ok(t); } else { todo!() @@ -585,20 +593,20 @@ impl Context { } } } - todo!( - "{lhs}.{rhs} not found in [{}]", - erg_common::fmt_iter( - self.get_nominal_super_type_ctxs(&lhs) - .unwrap() - .map(|(_, ctx)| &ctx.name) - ) - ) + let proj = mono_proj(*lhs, rhs); + Err(EvalError::no_candidate_error( + line!() as usize, + &proj, + t_loc, + self.caused_by(), + self.get_no_candidate_hint(&proj), + )) } - Type::Ref(l) => Ok(ref_(self.eval_t_params(*l, level)?)), + Type::Ref(l) => Ok(ref_(self.eval_t_params(*l, level, t_loc)?)), Type::RefMut { before, after } => { - let before = self.eval_t_params(*before, level)?; + let before = self.eval_t_params(*before, level, t_loc)?; let after = if let Some(after) = after { - Some(self.eval_t_params(*after, level)?) + Some(self.eval_t_params(*after, level, t_loc)?) } else { None }; @@ -615,17 +623,23 @@ impl Context { } } - pub(crate) fn _eval_bound(&self, bound: TyBound, level: usize) -> EvalResult { + pub(crate) fn _eval_bound( + &self, + bound: TyBound, + level: usize, + t_loc: Location, + ) -> EvalResult { match bound { TyBound::Sandwiched { sub, mid, sup } => { - let sub = self.eval_t_params(sub, level)?; - let mid = self.eval_t_params(mid, level)?; - let sup = self.eval_t_params(sup, level)?; + let sub = self.eval_t_params(sub, level, t_loc)?; + let mid = self.eval_t_params(mid, level, t_loc)?; + let sup = self.eval_t_params(sup, level, t_loc)?; Ok(TyBound::sandwiched(sub, mid, sup)) } - TyBound::Instance { name: inst, t } => { - Ok(TyBound::instance(inst, self.eval_t_params(t, level)?)) - } + TyBound::Instance { name: inst, t } => Ok(TyBound::instance( + inst, + self.eval_t_params(t, level, t_loc)?, + )), } } diff --git a/compiler/erg_compiler/context/hint.rs b/compiler/erg_compiler/context/hint.rs index 38999882..fa0d659b 100644 --- a/compiler/erg_compiler/context/hint.rs +++ b/compiler/erg_compiler/context/hint.rs @@ -1,11 +1,36 @@ -use erg_common::Str; +use erg_common::astr::AtomicStr; +use erg_common::enum_unwrap; +use erg_type::typaram::TyParam; use erg_type::Type; use crate::context::Context; +#[derive(PartialEq, Eq)] +enum Sequence { + Forward, + Backward, +} + impl Context { - pub(crate) fn get_type_mismatch_hint(&self, expected: &Type, found: &Type) -> Option { + fn readable_type(&self, typ: &Type) -> Type { + match typ { + Type::FreeVar(fv) if fv.constraint_is_sandwiched() => { + let (sub, sup) = fv.get_bound_types().unwrap(); + if sup == Type::Obj { + return sub; + } + Type::FreeVar(fv.clone()) + } + other => other.clone(), + } + } + + pub(crate) fn get_type_mismatch_hint( + &self, + expected: &Type, + found: &Type, + ) -> Option { let expected = if let Type::FreeVar(fv) = expected { if fv.is_linked() { fv.crack().clone() @@ -17,7 +42,38 @@ impl Context { expected.clone() }; match (&expected.name()[..], &found.name()[..]) { - ("Eq", "Float") => Some(Str::ever("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")), + ("Eq", "Float") => Some(AtomicStr::ever("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")), + _ => None, + } + } + + pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option { + match proj { + Type::MonoProj { lhs, rhs: _ } => { + if let Type::FreeVar(fv) = lhs.as_ref() { + let (sub, sup) = fv.get_bound_types()?; + // TODO: automating + let (verb, preposition, sequence) = match &sup.name()[..] { + "Add" => Some(("add", "and", Sequence::Forward)), + "Sub" => Some(("subtract", "from", Sequence::Backward)), + "Mul" => Some(("multiply", "and", Sequence::Forward)), + "Div" => Some(("divide", "by", Sequence::Forward)), + _ => None, + }?; + let sup = enum_unwrap!(sup.typarams().remove(0), TyParam::Type); + let sup = self.readable_type(&sup); + let (l, r) = if sequence == Sequence::Forward { + (sub, sup) + } else { + (sup, sub) + }; + Some(AtomicStr::from(format!( + "cannot {verb} {l} {preposition} {r}" + ))) + } else { + None + } + } _ => None, } } diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index e4972c5f..9601c60d 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -1594,7 +1594,7 @@ impl Context { op_t, set! { static_instance("R", Type), - subtypeof(l, poly("Mul", params)) + subtypeof(l, poly("Div", params)) }, ); self.register_builtin_impl("__div__", op_t, Const, Private); diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index eab76aa3..4106580d 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -100,7 +100,7 @@ impl Context { Err(TyCheckError::no_var_error( line!() as usize, obj.loc(), - namespace.clone(), + namespace.into(), ident.inspect(), self.get_similar_name(ident.inspect()), )) @@ -213,7 +213,7 @@ impl Context { Err(TyCheckError::no_var_error( line!() as usize, ident.loc(), - namespace.clone(), + namespace.into(), ident.inspect(), self.get_similar_name(ident.inspect()), )) @@ -269,7 +269,7 @@ impl Context { Err(TyCheckError::no_attr_error( line!() as usize, name.loc(), - namespace.clone(), + namespace.into(), &self_t, name.inspect(), self.get_similar_attr(&self_t, name.inspect()), @@ -308,7 +308,7 @@ impl Context { Err(TyCheckError::no_attr_error( line!() as usize, ident.loc(), - namespace.clone(), + namespace.into(), &t, ident.inspect(), self.get_similar_attr(&t, ident.inspect()), @@ -393,7 +393,7 @@ impl Context { return Err(TyCheckError::singular_no_attr_error( line!() as usize, method_name.loc(), - namespace.clone(), + namespace.into(), obj.__name__().unwrap_or("?"), obj.ref_t(), method_name.inspect(), @@ -404,7 +404,7 @@ impl Context { Err(TyCheckError::no_attr_error( line!() as usize, method_name.loc(), - namespace.clone(), + namespace.into(), obj.ref_t(), method_name.inspect(), self.get_similar_attr(obj.ref_t(), method_name.inspect()), @@ -644,7 +644,12 @@ impl Context { let new_r = self.resolve_trait(*r)?; Ok(self.intersection(&new_l, &new_r)) } - Type::Or(_, _) | Type::Not(_, _) => todo!(), + Type::Or(l, r) => { + let new_l = self.resolve_trait(*l)?; + let new_r = self.resolve_trait(*r)?; + Ok(self.union(&new_l, &new_r)) + } + Type::Not(_, _) => todo!(), other => Ok(other), } } @@ -951,7 +956,7 @@ impl Context { ); self.substitute_call(obj, method_name, &instance, pos_args, kw_args)?; log!(info "Substituted:\ninstance: {instance}"); - let res = self.eval_t_params(instance, self.level)?; + let res = self.eval_t_params(instance, self.level, obj.loc())?; log!(info "Params evaluated:\nres: {res}\n"); self.propagate(&res, obj)?; log!(info "Propagated:\nres: {res}\n"); @@ -970,7 +975,7 @@ impl Context { Err(TyCheckError::no_var_error( line!() as usize, name.loc(), - namespace.clone(), + namespace.into(), name.inspect(), self.get_similar_name(name.inspect()), )) @@ -999,7 +1004,7 @@ impl Context { Err(TyCheckError::no_attr_error( line!() as usize, name.loc(), - namespace.clone(), + namespace.into(), self_t, name.inspect(), self.get_similar_attr(self_t, name.inspect()), @@ -1515,8 +1520,12 @@ impl Context { let candidates = insts.into_iter().filter_map(move |inst| { if self.supertype_of(&inst.sup_trait, &sup) { Some( - self.eval_t_params(mono_proj(inst.sub_type, rhs), self.level) - .unwrap(), + self.eval_t_params( + mono_proj(inst.sub_type, rhs), + self.level, + Location::Unknown, + ) + .unwrap(), ) } else { None diff --git a/compiler/erg_compiler/context/mod.rs b/compiler/erg_compiler/context/mod.rs index fea2fb12..155acdd9 100644 --- a/compiler/erg_compiler/context/mod.rs +++ b/compiler/erg_compiler/context/mod.rs @@ -17,6 +17,7 @@ use std::fmt; use std::mem; use std::option::Option; // conflicting to Type::Option +use erg_common::astr::AtomicStr; use erg_common::dict::Dict; use erg_common::error::Location; use erg_common::impl_display_from_debug; @@ -476,8 +477,8 @@ impl Context { } #[inline] - pub fn caused_by(&self) -> Str { - self.name.clone() + pub fn caused_by(&self) -> AtomicStr { + AtomicStr::arc(&self.name[..]) } pub(crate) fn grow( diff --git a/compiler/erg_compiler/effectcheck.rs b/compiler/erg_compiler/effectcheck.rs index 0514817b..7f8af2a3 100644 --- a/compiler/erg_compiler/effectcheck.rs +++ b/compiler/erg_compiler/effectcheck.rs @@ -106,6 +106,37 @@ impl SideEffectChecker { self.check_expr(&unary.expr); } Expr::Accessor(_) | Expr::Lit(_) => {} + Expr::Array(array) => match array { + Array::Normal(arr) => { + for elem in arr.elems.pos_args.iter() { + self.check_expr(&elem.expr); + } + } + Array::WithLength(arr) => { + self.check_expr(&arr.elem); + self.check_expr(&arr.len); + } + Array::Comprehension(arr) => { + self.check_expr(&arr.elem); + self.check_expr(&arr.guard); + } + }, + Expr::Tuple(tuple) => match tuple { + Tuple::Normal(tuple) => { + for elem in tuple.elems.pos_args.iter() { + self.check_expr(&elem.expr); + } + } + }, + Expr::Record(rec) => { + self.path_stack.push((Str::ever(""), Private)); + self.block_stack.push(Instant); + for attr in rec.attrs.iter() { + self.check_def(attr); + } + self.path_stack.pop(); + self.block_stack.pop(); + } other => todo!("{other}"), } } @@ -187,11 +218,18 @@ impl SideEffectChecker { } Expr::Array(array) => match array { Array::Normal(arr) => { - for arg in arr.elems.pos_args.iter() { - self.check_expr(&arg.expr); + for elem in arr.elems.pos_args.iter() { + self.check_expr(&elem.expr); } } - other => todo!("{other}"), + Array::WithLength(arr) => { + self.check_expr(&arr.elem); + self.check_expr(&arr.len); + } + Array::Comprehension(arr) => { + self.check_expr(&arr.elem); + self.check_expr(&arr.guard); + } }, Expr::Tuple(tuple) => match tuple { Tuple::Normal(tup) => { @@ -201,9 +239,13 @@ impl SideEffectChecker { } }, Expr::Record(record) => { + self.path_stack.push((Str::ever(""), Private)); + self.block_stack.push(Instant); for attr in record.attrs.iter() { self.check_def(attr); } + self.path_stack.pop(); + self.block_stack.pop(); } // 引数がproceduralでも関数呼び出しなら副作用なし Expr::Call(call) => { diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index d44290c5..d9dde557 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -1,6 +1,7 @@ use std::fmt::Display; use std::ops::Add; +use erg_common::astr::AtomicStr; use erg_common::color::{GREEN, RED, RESET, YELLOW}; use erg_common::config::Input; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; @@ -96,7 +97,7 @@ pub fn readable_name(name: &str) -> &str { pub struct CompileError { pub core: ErrorCore, pub input: Input, - pub caused_by: Str, + pub caused_by: AtomicStr, } impl From for CompileError { @@ -125,7 +126,7 @@ impl ErrorDisplay for CompileError { } impl CompileError { - pub const fn new(core: ErrorCore, input: Input, caused_by: Str) -> Self { + pub const fn new(core: ErrorCore, input: Input, caused_by: AtomicStr) -> Self { Self { core, input, @@ -191,7 +192,7 @@ impl CompileError { ) } - pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: Str) -> Self { + pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: AtomicStr) -> Self { Self::new( ErrorCore::new( 0, @@ -233,7 +234,7 @@ impl CompileError { #[derive(Debug)] pub struct TyCheckError { pub core: ErrorCore, - pub caused_by: Str, + pub caused_by: AtomicStr, } impl ErrorDisplay for TyCheckError { @@ -252,7 +253,7 @@ impl ErrorDisplay for TyCheckError { } impl TyCheckError { - pub const fn new(core: ErrorCore, caused_by: Str) -> Self { + pub const fn new(core: ErrorCore, caused_by: AtomicStr) -> Self { Self { core, caused_by } } @@ -282,7 +283,7 @@ impl TyCheckError { ) } - pub fn feature_error(errno: usize, loc: Location, name: &str, caused_by: Str) -> Self { + pub fn feature_error(errno: usize, loc: Location, name: &str, caused_by: AtomicStr) -> Self { Self::new( ErrorCore::new( errno, @@ -300,12 +301,12 @@ impl TyCheckError { ) } - pub fn syntax_error>( + pub fn syntax_error>( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, desc: S, - hint: Option, + hint: Option, ) -> Self { Self::new( ErrorCore::new(errno, SyntaxError, loc, desc, hint), @@ -313,7 +314,12 @@ impl TyCheckError { ) } - pub fn duplicate_decl_error(errno: usize, loc: Location, caused_by: Str, name: &str) -> Self { + pub fn duplicate_decl_error( + errno: usize, + loc: Location, + caused_by: AtomicStr, + name: &str, + ) -> Self { let name = readable_name(name); Self::new( ErrorCore::new( @@ -326,7 +332,7 @@ impl TyCheckError { "traditional_chinese" => format!("{name}已聲明"), "english" => format!("{name} is already declared"), ), - Option::::None, + Option::::None, ), caused_by, ) @@ -335,7 +341,7 @@ impl TyCheckError { pub fn duplicate_definition_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, ) -> Self { let name = readable_name(name); @@ -350,7 +356,7 @@ impl TyCheckError { "traditional_chinese" => format!("{name}已定義"), "english" => format!("{name} is already defined"), ), - Option::::None, + Option::::None, ), caused_by, ) @@ -359,7 +365,7 @@ impl TyCheckError { pub fn violate_decl_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, spec_t: &Type, found_t: &Type, @@ -376,13 +382,18 @@ impl TyCheckError { "traditional_chinese" => format!("{name}被聲明為{GREEN}{spec_t}{RESET},但分配了一個{RED}{found_t}{RESET}對象"), "english" => format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"), ), - Option::::None, + Option::::None, ), caused_by, ) } - pub fn no_type_spec_error(errno: usize, loc: Location, caused_by: Str, name: &str) -> Self { + pub fn no_type_spec_error( + errno: usize, + loc: Location, + caused_by: AtomicStr, + name: &str, + ) -> Self { let name = readable_name(name); Self::new( ErrorCore::new( @@ -404,7 +415,7 @@ impl TyCheckError { pub fn no_var_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, similar_name: Option<&Str>, ) -> Self { @@ -439,7 +450,7 @@ impl TyCheckError { pub fn no_attr_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, obj_t: &Type, name: &str, similar_name: Option<&Str>, @@ -474,7 +485,7 @@ impl TyCheckError { pub fn singular_no_attr_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, obj_name: &str, obj_t: &Type, name: &str, @@ -511,7 +522,7 @@ impl TyCheckError { errno: usize, callee: &C, param_ts: impl Iterator, - caused_by: Str, + caused_by: AtomicStr, ) -> Self { let param_ts = fmt_iter(param_ts); Self::new( @@ -539,12 +550,12 @@ impl TyCheckError { pub fn type_mismatch_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, expect: &Type, found: &Type, candidates: Option>, - hint: Option, + hint: Option, ) -> Self { Self::new( ErrorCore::new( @@ -566,7 +577,7 @@ impl TyCheckError { pub fn return_type_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, expect: &Type, found: &Type, @@ -591,7 +602,7 @@ impl TyCheckError { pub fn uninitialized_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, t: &Type, ) -> Self { @@ -615,7 +626,7 @@ impl TyCheckError { pub fn argument_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, expect: usize, found: usize, ) -> Self { @@ -636,7 +647,7 @@ impl TyCheckError { ) } - pub fn match_error(errno: usize, loc: Location, caused_by: Str, expr_t: &Type) -> Self { + pub fn match_error(errno: usize, loc: Location, caused_by: AtomicStr, expr_t: &Type) -> Self { Self::new( ErrorCore::new( errno, @@ -654,7 +665,7 @@ impl TyCheckError { ) } - pub fn infer_error(errno: usize, loc: Location, caused_by: Str, expr: &str) -> Self { + pub fn infer_error(errno: usize, loc: Location, caused_by: AtomicStr, expr: &str) -> Self { Self::new( ErrorCore::new( errno, @@ -680,7 +691,7 @@ impl TyCheckError { Self::new(ErrorCore::unreachable(fn_name, line), "".into()) } - pub fn reassign_error(errno: usize, loc: Location, caused_by: Str, name: &str) -> Self { + pub fn reassign_error(errno: usize, loc: Location, caused_by: AtomicStr, name: &str) -> Self { let name = readable_name(name); Self::new( ErrorCore::new( @@ -703,7 +714,7 @@ impl TyCheckError { errno: usize, loc: Location, callee_name: &str, - caused_by: Str, + caused_by: AtomicStr, params_len: usize, pos_args_len: usize, kw_args_len: usize, @@ -750,7 +761,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" errno: usize, loc: Location, callee_name: &str, - caused_by: Str, + caused_by: AtomicStr, missing_len: usize, missing_params: Vec, ) -> Self { @@ -776,7 +787,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" errno: usize, loc: Location, callee_name: &str, - caused_by: Str, + caused_by: AtomicStr, arg_name: &str, ) -> Self { let name = readable_name(callee_name); @@ -801,7 +812,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" errno: usize, loc: Location, callee_name: &str, - caused_by: Str, + caused_by: AtomicStr, param_name: &str, ) -> Self { let name = readable_name(callee_name); @@ -822,7 +833,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn unused_warning(errno: usize, loc: Location, name: &str, caused_by: Str) -> Self { + pub fn unused_warning(errno: usize, loc: Location, name: &str, caused_by: AtomicStr) -> Self { let name = readable_name(name); Self::new( ErrorCore::new( @@ -847,7 +858,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" rhs_t: &Type, lhs_loc: Option, rhs_loc: Option, - caused_by: Str, + caused_by: AtomicStr, ) -> Self { let loc = match (lhs_loc, rhs_loc) { (Some(l), Some(r)) => Location::pair(l, r), @@ -878,7 +889,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" rhs_t: &Type, lhs_loc: Option, rhs_loc: Option, - caused_by: Str, + caused_by: AtomicStr, ) -> Self { let loc = match (lhs_loc, rhs_loc) { (Some(l), Some(r)) => Location::pair(l, r), @@ -909,7 +920,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" sup_t: &Type, sub_loc: Option, sup_loc: Option, - caused_by: Str, + caused_by: AtomicStr, ) -> Self { let loc = match (sub_loc, sup_loc) { (Some(l), Some(r)) => Location::pair(l, r), @@ -938,7 +949,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" errno: usize, lhs: &Predicate, rhs: &Predicate, - caused_by: Str, + caused_by: AtomicStr, ) -> Self { Self::new( ErrorCore::new( @@ -957,7 +968,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn has_effect>(errno: usize, expr: &Expr, caused_by: S) -> Self { + pub fn has_effect>(errno: usize, expr: &Expr, caused_by: S) -> Self { Self::new( ErrorCore::new( errno, @@ -975,7 +986,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn move_error>( + pub fn move_error>( errno: usize, name: &str, name_loc: Location, @@ -1014,7 +1025,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" pub fn visibility_error( errno: usize, loc: Location, - caused_by: Str, + caused_by: AtomicStr, name: &str, vis: Visibility, ) -> Self { @@ -1051,7 +1062,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn not_const_expr(errno: usize, loc: Location, caused_by: Str) -> Self { + pub fn not_const_expr(errno: usize, loc: Location, caused_by: AtomicStr) -> Self { Self::new( ErrorCore::new( errno, @@ -1069,7 +1080,7 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn override_error>( + pub fn override_error>( errno: usize, name: &str, name_loc: Location, @@ -1106,7 +1117,12 @@ passed keyword args: {RED}{kw_args_len}{RESET}" ) } - pub fn inheritance_error(errno: usize, class: String, loc: Location, caused_by: Str) -> Self { + pub fn inheritance_error( + errno: usize, + class: String, + loc: Location, + caused_by: AtomicStr, + ) -> Self { Self::new( ErrorCore::new( errno, @@ -1123,6 +1139,30 @@ passed keyword args: {RED}{kw_args_len}{RESET}" caused_by, ) } + + pub fn no_candidate_error( + errno: usize, + proj: &Type, + loc: Location, + caused_by: AtomicStr, + hint: Option, + ) -> Self { + Self::new( + ErrorCore::new( + errno, + TypeError, + loc, + switch_lang!( + "japanese" => format!("{proj}の候補がありません"), + "simplified_chinese" => format!("{proj}没有候选项"), + "traditional_chinese" => format!("{proj}沒有候選項"), + "english" => format!("no candidate for {proj}"), + ), + hint, + ), + caused_by, + ) + } } #[derive(Debug)] diff --git a/compiler/erg_compiler/hir.rs b/compiler/erg_compiler/hir.rs index 3ffab608..82a9ccf7 100644 --- a/compiler/erg_compiler/hir.rs +++ b/compiler/erg_compiler/hir.rs @@ -568,6 +568,7 @@ impl ArrayWithLength { } } +// TODO: generators #[derive(Debug, Clone)] pub struct ArrayComprehension { pub l_sqbr: Token, diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index d3bdf656..df9dc02c 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -1,6 +1,7 @@ //! implements `ASTLowerer`. //! //! ASTLowerer(ASTからHIRへの変換器)を実装 +use erg_common::astr::AtomicStr; use erg_common::config::{ErgConfig, Input}; use erg_common::error::{Location, MultiErrorDisplay}; use erg_common::traits::{Locational, Runnable, Stream}; @@ -17,7 +18,7 @@ use erg_parser::Parser; use erg_type::constructors::{array, array_mut, free_var, func, mono, poly, proc, quant}; use erg_type::free::Constraint; use erg_type::typaram::TyParam; -use erg_type::value::{TypeObj, ValueObj}; +use erg_type::value::{GenTypeObj, TypeObj, ValueObj}; use erg_type::{HasType, ParamTy, Type}; use crate::context::{ClassDefType, Context, ContextKind, RegistrationMode}; @@ -30,34 +31,6 @@ use crate::link::Linker; use crate::varinfo::VarKind; use Visibility::*; -/// HACK: Cannot be methodized this because a reference has been taken immediately before. -macro_rules! check_inheritable { - ($self: ident, $type_obj: expr, $sup_class: expr, $sub_sig: expr) => { - match $type_obj.require_or_sup.as_ref() { - TypeObj::Generated(gen) => { - if let Some(impls) = gen.impls.as_ref() { - if !impls.contains_intersec(&mono("InheritableType")) { - $self.errs.push(LowerError::inheritance_error( - line!() as usize, - $sup_class.to_string(), - $sup_class.loc(), - $sub_sig.ident().inspect().clone(), - )); - } - } else { - $self.errs.push(LowerError::inheritance_error( - line!() as usize, - $sup_class.to_string(), - $sup_class.loc(), - $sub_sig.ident().inspect().clone(), - )); - } - } - _ => {} - } - }; -} - pub struct ASTLowererRunner { cfg: ErgConfig, lowerer: ASTLowerer, @@ -109,7 +82,7 @@ impl Runnable for ASTLowererRunner { Ok(()) } - fn eval(&mut self, src: Str) -> Result { + fn eval(&mut self, src: String) -> Result { let ts = Lexer::new(Input::Str(src)) .lex() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; @@ -186,7 +159,7 @@ impl ASTLowerer { Err(LowerError::syntax_error( 0, expr.loc(), - self.ctx.name.clone(), + AtomicStr::arc(&self.ctx.name[..]), switch_lang!( "japanese" => format!("式の評価結果(: {})が使われていません", expr.ref_t()), "simplified_chinese" => format!("表达式评估结果(: {})未使用", expr.ref_t()), @@ -237,7 +210,7 @@ impl ASTLowerer { return Err(LowerError::syntax_error( line!() as usize, elem.loc(), - self.ctx.name.clone(), + AtomicStr::arc(&self.ctx.name[..]), switch_lang!( "japanese" => "配列の要素は全て同じ型である必要があります", "simplified_chinese" => "数组元素必须全部是相同类型", @@ -750,7 +723,7 @@ impl ASTLowerer { self.errs.push(LowerError::duplicate_definition_error( line!() as usize, newly_defined_name.loc(), - methods.name.clone(), + methods.name.clone().into(), newly_defined_name.inspect(), )); } else { @@ -782,7 +755,7 @@ impl ASTLowerer { .args .get_left_or_key("Super") .unwrap(); - check_inheritable!(self, type_obj, sup_type, &hir_def.sig); + Self::check_inheritable(&mut self.errs, type_obj, sup_type, &hir_def.sig); // vi.t.non_default_params().unwrap()[0].typ().clone() let (__new__, need_to_gen_new) = if let (Some(dunder_new_vi), Some(new_vi)) = ( ctx.get_current_scope_var("__new__"), @@ -804,6 +777,34 @@ impl ASTLowerer { )) } + /// HACK: Cannot be methodized this because `&self` has been taken immediately before. + fn check_inheritable( + errs: &mut LowerErrors, + type_obj: &GenTypeObj, + sup_class: &hir::Expr, + sub_sig: &hir::Signature, + ) { + if let TypeObj::Generated(gen) = type_obj.require_or_sup.as_ref() { + if let Some(impls) = gen.impls.as_ref() { + if !impls.contains_intersec(&mono("InheritableType")) { + errs.push(LowerError::inheritance_error( + line!() as usize, + sup_class.to_string(), + sup_class.loc(), + sub_sig.ident().inspect().into(), + )); + } + } else { + errs.push(LowerError::inheritance_error( + line!() as usize, + sup_class.to_string(), + sup_class.loc(), + sub_sig.ident().inspect().into(), + )); + } + } + } + fn check_override(&mut self, class: &Type, ctx: &Context) { if let Some(sups) = self.ctx.get_nominal_super_type_ctxs(class) { for (sup_t, sup) in sups.skip(1) { diff --git a/compiler/erg_parser/Cargo.toml b/compiler/erg_parser/Cargo.toml index 81e3e451..ebfa62e1 100644 --- a/compiler/erg_parser/Cargo.toml +++ b/compiler/erg_parser/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "erg_parser" -version = "0.4.1" +version = "0.4.6" description = "The Erg parser" -authors = ["mtshiba "] +authors = ["erg-lang team "] license = "MIT OR Apache-2.0" edition = "2021" repository = "https://github.com/erg-lang/erg/tree/main/src/erg_compiler/erg_parser" @@ -16,7 +16,7 @@ simplified_chinese = [ "erg_common/simplified_chinese" ] traditional_chinese = [ "erg_common/traditional_chinese" ] [dependencies] -erg_common = { version = "0.4.1", path = "../erg_common" } +erg_common = { version = "0.4.6", path = "../erg_common" } [lib] path = "lib.rs" diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index ccd1a611..692d0722 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -2138,10 +2138,55 @@ impl ParamTuplePattern { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamRecordAttr { + pub lhs: Identifier, + pub rhs: ParamSignature, +} + +impl NestedDisplay for ParamRecordAttr { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{} = {}", self.lhs, self.rhs) + } +} + +impl_display_from_nested!(ParamRecordAttr); +impl_locational!(ParamRecordAttr, lhs, rhs); + +impl ParamRecordAttr { + pub const fn new(lhs: Identifier, rhs: ParamSignature) -> Self { + Self { lhs, rhs } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamRecordAttrs { + pub(crate) elems: Vec, +} + +impl NestedDisplay for ParamRecordAttrs { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}", fmt_vec_split_with(&self.elems, "; ")) + } +} + +impl_display_from_nested!(ParamRecordAttrs); +impl_stream!(ParamRecordAttrs, ParamRecordAttr, elems); + +impl ParamRecordAttrs { + pub const fn new(elems: Vec) -> Self { + Self { elems } + } + + pub const fn empty() -> Self { + Self::new(vec![]) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct ParamRecordPattern { l_brace: Token, - pub(crate) elems: Params, + pub(crate) elems: ParamRecordAttrs, r_brace: Token, } @@ -2155,7 +2200,7 @@ impl_display_from_nested!(ParamRecordPattern); impl_locational!(ParamRecordPattern, l_brace, r_brace); impl ParamRecordPattern { - pub const fn new(l_brace: Token, elems: Params, r_brace: Token) -> Self { + pub const fn new(l_brace: Token, elems: ParamRecordAttrs, r_brace: Token) -> Self { Self { l_brace, elems, diff --git a/compiler/erg_parser/error.rs b/compiler/erg_parser/error.rs index d4345879..bd1b207e 100644 --- a/compiler/erg_parser/error.rs +++ b/compiler/erg_parser/error.rs @@ -1,10 +1,10 @@ //! defines `ParseError` and others. //! //! パーサーが出すエラーを定義 +use erg_common::astr::AtomicStr; use erg_common::config::Input; use erg_common::error::{ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay}; use erg_common::traits::Stream; -use erg_common::Str; use erg_common::{impl_stream_for_wrapper, switch_lang}; #[derive(Debug)] @@ -65,20 +65,20 @@ impl LexError { )) } - pub fn syntax_error>( + pub fn syntax_error>( errno: usize, loc: Location, desc: S, - hint: Option, + hint: Option, ) -> Self { Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint)) } - pub fn syntax_warning>( + pub fn syntax_warning>( errno: usize, loc: Location, desc: S, - hint: Option, + hint: Option, ) -> Self { Self::new(ErrorCore::new(errno, SyntaxWarning, loc, desc, hint)) } diff --git a/compiler/erg_parser/lex.rs b/compiler/erg_parser/lex.rs index f3ef15dc..440bb2f9 100644 --- a/compiler/erg_parser/lex.rs +++ b/compiler/erg_parser/lex.rs @@ -5,7 +5,6 @@ use erg_common::cache::CacheSet; use erg_common::config::ErgConfig; use erg_common::config::Input; use erg_common::traits::{Locational, Runnable, Stream}; -use erg_common::Str; use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang}; use crate::error::{LexError, LexErrors, LexResult, LexerRunnerError, LexerRunnerErrors}; @@ -47,7 +46,7 @@ impl Runnable for LexerRunner { Ok(()) } - fn eval(&mut self, src: Str) -> Result { + fn eval(&mut self, src: String) -> Result { let lexer = Lexer::from_str(src); if cfg!(feature = "debug") { let ts = lexer @@ -97,7 +96,7 @@ impl Lexer /*<'a>*/ { } #[allow(clippy::should_implement_trait)] - pub fn from_str(src: Str) -> Self { + pub fn from_str(src: String) -> Self { let escaped = normalize_newline(&src); Lexer { str_cache: CacheSet::new(), diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 1393e63c..7adcb024 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -194,7 +194,7 @@ impl Runnable for ParserRunner { Ok(()) } - fn eval(&mut self, src: Str) -> Result { + fn eval(&mut self, src: String) -> Result { let ast = self.parse_with_input(src)?; Ok(format!("{ast}")) } @@ -224,7 +224,7 @@ impl ParserRunner { self_.parse() } - fn parse_with_input(&mut self, src: Str) -> Result { + fn parse_with_input(&mut self, src: String) -> Result { let ts = Lexer::new(Input::Str(src)) .lex() .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; @@ -309,18 +309,21 @@ impl Parser { self.level -= 1; return Ok(block); } + assert!(self.cur_is(Newline)); + self.skip(); + assert!(self.cur_is(Indent)); + self.skip(); loop { match self.peek() { + Some(t) if t.is(Newline) && self.nth_is(1, Dedent) => { + let nl = self.lpop(); + self.skip(); + self.restore(nl); + break; + } Some(t) if t.category_is(TC::Separator) => { self.skip(); } - Some(t) if t.is(Indent) => { - self.skip(); - } - Some(t) if t.is(Dedent) => { - self.skip(); - break; - } Some(t) if t.is(EOF) => { break; } @@ -391,7 +394,7 @@ impl Parser { fn try_reduce_acc(&mut self) -> ParseResult { debug_call_info!(self); let mut acc = match self.peek() { - Some(t) if t.is(Symbol) => Accessor::local(self.lpop()), + Some(t) if t.is(Symbol) || t.is(UBar) => Accessor::local(self.lpop()), Some(t) if t.is(Dot) => { let dot = self.lpop(); let maybe_symbol = self.lpop(); @@ -609,7 +612,8 @@ impl Parser { || t.category_is(TC::UnaryOp) || t.is(LParen) || t.is(LSqBr) - || t.is(LBrace) => + || t.is(LBrace) + || t.is(UBar) => { Some(self.try_reduce_args()) } @@ -670,18 +674,6 @@ impl Parser { } debug_power_assert!(self.cur_is(Indent)); self.skip(); - if !args.kw_is_empty() { - args.push_kw(self.try_reduce_kw_arg().map_err(|_| self.stack_dec())?); - } else { - match self.try_reduce_arg().map_err(|_| self.stack_dec())? { - PosOrKwArg::Pos(arg) => { - args.push_pos(arg); - } - PosOrKwArg::Kw(arg) => { - args.push_kw(arg); - } - } - } } Some(t) if t.is(Comma) => { self.skip(); @@ -704,14 +696,24 @@ impl Parser { } } } - Some(t) if t.is(Newline) && colon_style => { - while self.cur_is(Newline) { - self.skip(); - } - if self.cur_is(Dedent) { - self.skip(); + Some(t) if t.is(RParen) => { + rp = Some(self.lpop()); + let (pos_args, kw_args, _) = args.deconstruct(); + args = Args::new(pos_args, kw_args, Some((lp.unwrap(), rp.unwrap()))); + break; + } + Some(t) if t.is(Newline) => { + if !colon_style { break; } + let last = self.lpop(); + if self.cur_is(Dedent) { + self.skip(); + self.restore(last); + break; + } + } + Some(_) if colon_style => { if !args.kw_is_empty() { args.push_kw(self.try_reduce_kw_arg().map_err(|_| self.stack_dec())?); } else { @@ -725,12 +727,6 @@ impl Parser { } } } - Some(t) if t.is(RParen) => { - rp = Some(self.lpop()); - let (pos_args, kw_args, _) = args.deconstruct(); - args = Args::new(pos_args, kw_args, Some((lp.unwrap(), rp.unwrap()))); - break; - } _ => { break; } @@ -763,7 +759,7 @@ impl Parser { self.errs.push(err); return Err(()); }; - let t_spec = if self.cur_is(Colon) { + /*let t_spec = if self.cur_is(Colon) { self.skip(); let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; let t_spec = self @@ -772,10 +768,10 @@ impl Parser { Some(t_spec) } else { None - }; + };*/ let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(PosOrKwArg::Kw(KwArg::new(kw, t_spec, expr))) + Ok(PosOrKwArg::Kw(KwArg::new(kw, None, expr))) } else { let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; self.level -= 1; @@ -808,7 +804,7 @@ impl Parser { .push(ParseError::simple_syntax_error(0, acc.loc())); return Err(()); }; - let t_spec = if self.cur_is(Colon) { + /*let t_spec = if self.cur_is(Colon) { self.skip(); let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; let t_spec = self @@ -817,10 +813,10 @@ impl Parser { Some(t_spec) } else { None - }; + };*/ let expr = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; self.level -= 1; - Ok(KwArg::new(keyword, t_spec, expr)) + Ok(KwArg::new(keyword, None, expr)) } else { let loc = t.loc(); self.level -= 1; @@ -853,13 +849,15 @@ impl Parser { let mut defs = vec![first]; loop { match self.peek() { + Some(t) if t.is(Newline) && self.nth_is(1, Dedent) => { + let nl = self.lpop(); + self.skip(); + self.restore(nl); + break; + } Some(t) if t.category_is(TC::Separator) => { self.skip(); } - Some(t) if t.is(Dedent) => { - self.skip(); - break; - } Some(_) => { let def = self.try_reduce_chunk(false).map_err(|_| self.stack_dec())?; match def { @@ -906,6 +904,7 @@ impl Parser { } } + /// chunk = normal expr + def fn try_reduce_chunk(&mut self, winding: bool) -> ParseResult { debug_call_info!(self); let mut stack = Vec::::new(); @@ -926,7 +925,8 @@ impl Parser { self.counter.inc(); let block = self.try_reduce_block().map_err(|_| self.stack_dec())?; let body = DefBody::new(op, block, self.counter); - stack.push(ExprOrOp::Expr(Expr::Def(Def::new(sig, body)))); + self.level -= 1; + return Ok(Expr::Def(Def::new(sig, body))); } Some(op) if op.category_is(TC::LambdaOp) => { let op = self.lpop(); @@ -943,7 +943,7 @@ impl Parser { self.counter, )))); } - Some(op) if op.is(Colon) => { + Some(op) if op.is(Colon) && !self.nth_is(1, Newline) => { let _op = self.lpop(); let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; @@ -1139,6 +1139,7 @@ impl Parser { } } + /// chunk = expr + def /// winding: true => parse paren-less tuple fn try_reduce_expr(&mut self, winding: bool) -> ParseResult { debug_call_info!(self); @@ -1163,7 +1164,7 @@ impl Parser { self.counter, )))); } - Some(op) if op.is(Colon) => { + Some(op) if op.is(Colon) && !self.nth_is(1, Newline) => { let _op = self.lpop(); let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); let t_spec = self.try_reduce_expr(false).map_err(|_| self.stack_dec())?; @@ -1325,7 +1326,7 @@ impl Parser { } } } - Some(t) if t.is(Symbol) || t.is(Dot) => { + Some(t) if t.is(Symbol) || t.is(Dot) || t.is(UBar) => { let call_or_acc = self .try_reduce_call_or_acc() .map_err(|_| self.stack_dec())?; @@ -1720,7 +1721,11 @@ impl Parser { debug_call_info!(self); match accessor { Accessor::Ident(ident) => { - let pat = VarPattern::Ident(ident); + let pat = if &ident.inspect()[..] == "_" { + VarPattern::Discard(ident.name.into_token()) + } else { + VarPattern::Ident(ident) + }; self.level -= 1; Ok(VarSignature::new(pat, None)) } @@ -2054,10 +2059,41 @@ impl Parser { fn convert_record_to_param_record_pat( &mut self, - _record: Record, + record: Record, ) -> ParseResult { debug_call_info!(self); - todo!() + match record { + Record::Normal(rec) => { + let mut pats = vec![]; + for mut attr in rec.attrs.into_iter() { + let lhs = + option_enum_unwrap!(attr.sig, Signature::Var).unwrap_or_else(|| todo!()); + let lhs = + option_enum_unwrap!(lhs.pat, VarPattern::Ident).unwrap_or_else(|| todo!()); + assert_eq!(attr.body.block.len(), 1); + let rhs = option_enum_unwrap!(attr.body.block.remove(0), Expr::Accessor) + .unwrap_or_else(|| todo!()); + let rhs = self + .convert_accessor_to_param_sig(rhs) + .map_err(|_| self.stack_dec())?; + pats.push(ParamRecordAttr::new(lhs, rhs)); + } + let attrs = ParamRecordAttrs::new(pats); + self.level -= 1; + Ok(ParamRecordPattern::new(rec.l_brace, attrs, rec.r_brace)) + } + Record::Shortened(rec) => { + let mut pats = vec![]; + for ident in rec.idents.into_iter() { + let rhs = + ParamSignature::new(ParamPattern::VarName(ident.name.clone()), None, None); + pats.push(ParamRecordAttr::new(ident.clone(), rhs)); + } + let attrs = ParamRecordAttrs::new(pats); + self.level -= 1; + Ok(ParamRecordPattern::new(rec.l_brace, attrs, rec.r_brace)) + } + } } fn convert_tuple_to_param_tuple_pat(&mut self, tuple: Tuple) -> ParseResult { @@ -2146,7 +2182,11 @@ impl Parser { debug_call_info!(self); match accessor { Accessor::Ident(ident) => { - let pat = ParamPattern::VarName(ident.name); + let pat = if &ident.name.inspect()[..] == "_" { + ParamPattern::Discard(ident.name.into_token()) + } else { + ParamPattern::VarName(ident.name) + }; self.level -= 1; Ok(ParamSignature::new(pat, None, None)) } @@ -2185,10 +2225,18 @@ impl Parser { fn convert_type_asc_to_lambda_sig( &mut self, - _tasc: TypeAscription, + tasc: TypeAscription, ) -> ParseResult { debug_call_info!(self); - todo!() + let sig = self + .convert_rhs_to_param(*tasc.expr, true) + .map_err(|_| self.stack_dec())?; + self.level -= 1; + Ok(LambdaSignature::new( + Params::new(vec![sig], None, vec![], None), + None, + TypeBoundSpecs::empty(), + )) } fn convert_rhs_to_type_spec(&mut self, rhs: Expr) -> ParseResult { diff --git a/compiler/erg_parser/tests/parse_test.rs b/compiler/erg_parser/tests/parse_test.rs index 503d75f2..04aba987 100644 --- a/compiler/erg_parser/tests/parse_test.rs +++ b/compiler/erg_parser/tests/parse_test.rs @@ -49,6 +49,7 @@ fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerError dump_as_pyc: false, python_ver: None, py_server_timeout: 100, + quiet_startup: false, input: input.clone(), module: "", verbose: 2, diff --git a/compiler/erg_type/Cargo.toml b/compiler/erg_type/Cargo.toml index c37bc826..e3ea3719 100644 --- a/compiler/erg_type/Cargo.toml +++ b/compiler/erg_type/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "erg_type" -version = "0.4.1" +version = "0.4.6" description = "APIs for Erg types" -authors = ["Shunsuke Shibayama "] +authors = ["erg-lang team "] license = "MIT OR Apache-2.0" edition = "2021" repository = "https://github.com/erg-lang/erg/tree/main/compiler/erg_type" @@ -18,7 +18,7 @@ simplified_chinese = [ "erg_common/simplified_chinese" ] traditional_chinese = [ "erg_common/traditional_chinese" ] [dependencies] -erg_common = { version = "0.4.1", path = "../erg_common" } +erg_common = { version = "0.4.6", path = "../erg_common" } [lib] path = "lib.rs" diff --git a/compiler/erg_type/deserialize.rs b/compiler/erg_type/deserialize.rs index aca1df1a..1b47d006 100644 --- a/compiler/erg_type/deserialize.rs +++ b/compiler/erg_type/deserialize.rs @@ -2,6 +2,7 @@ use std::process; use std::string::FromUtf8Error; +use erg_common::astr::AtomicStr; use erg_common::cache::CacheSet; use erg_common::config::{ErgConfig, Input}; use erg_common::error::{ErrorCore, ErrorKind, Location}; @@ -18,8 +19,8 @@ use crate::{HasType, Type}; #[derive(Debug)] pub struct DeserializeError { pub errno: usize, - pub caused_by: Str, - pub desc: Str, + pub caused_by: AtomicStr, + pub desc: AtomicStr, } impl From for DeserializeError { @@ -41,13 +42,17 @@ impl From for ErrorCore { ErrorKind::ImportError, Location::Unknown, err.desc, - Option::::None, + Option::::None, ) } } impl DeserializeError { - pub fn new, T: Into>(errno: usize, caused_by: S, desc: T) -> Self { + pub fn new, T: Into>( + errno: usize, + caused_by: S, + desc: T, + ) -> Self { Self { errno, caused_by: caused_by.into(), diff --git a/compiler/erg_type/lib.rs b/compiler/erg_type/lib.rs index 6048b367..8454e5ec 100644 --- a/compiler/erg_type/lib.rs +++ b/compiler/erg_type/lib.rs @@ -500,6 +500,10 @@ impl Predicate { Self::Not(Box::new(lhs), Box::new(rhs)) } + pub fn is_equal(&self) -> bool { + matches!(self, Self::Equal { .. }) + } + pub fn subject(&self) -> Option<&str> { match self { Self::Equal { lhs, .. } @@ -878,9 +882,23 @@ impl LimitedDisplay for RefinementType { if limit == 0 { return write!(f, "..."); } - write!(f, "{{{}: ", self.var)?; - self.t.limited_fmt(f, limit - 1)?; - write!(f, " | {}}}", fmt_set_split_with(&self.preds, "; ")) + let first_subj = self.preds.iter().next().and_then(|p| p.subject()); + if self + .preds + .iter() + .all(|p| p.is_equal() && p.subject() == first_subj) + { + write!(f, "{{")?; + for pred in self.preds.iter() { + let (_, rhs) = enum_unwrap!(pred, Predicate::Equal { lhs, rhs }); + write!(f, "{}, ", rhs)?; + } + write!(f, "}}") + } else { + write!(f, "{{{}: ", self.var)?; + self.t.limited_fmt(f, limit - 1)?; + write!(f, " | {}}}", fmt_set_split_with(&self.preds, "; ")) + } } } diff --git a/doc/EN/syntax/04_function.md b/doc/EN/syntax/04_function.md index 8e93f12b..70a72ab9 100644 --- a/doc/EN/syntax/04_function.md +++ b/doc/EN/syntax/04_function.md @@ -32,13 +32,7 @@ f some_long_name_variable_1 + some_long_name_variable_2: some_long_name_variable_3 * some_long_name_variable_4 ``` -```python -f: - some_long_name_variable_1 + some_long_name_variable_2 - some_long_name_variable_3 * some_long_name_variable_4 -``` - -All three codes above mean the same thing. This style is also useful when using `if` functions, for example. +Codes above mean the same thing. This style is also useful when using `if` functions, for example. ```python result = if Bool.sample!(): @@ -51,6 +45,14 @@ result = if Bool.sample!(): ``` After `:`, no code other than comments may be written, and must always be on a new line. +Also, you cannot use `:` immediately after a function. Only `do` and `do!` can do this. + +```python +# NG +f: + x + y +``` ## Keyword Arguments diff --git a/doc/JA/syntax/04_function.md b/doc/JA/syntax/04_function.md index 0f756cba..2445f506 100644 --- a/doc/JA/syntax/04_function.md +++ b/doc/JA/syntax/04_function.md @@ -34,13 +34,7 @@ f some_long_name_variable_1 + some_long_name_variable_2: some_long_name_variable_3 * some_long_name_variable_4 ``` -```python -f: - some_long_name_variable_1 + some_long_name_variable_2 - some_long_name_variable_3 * some_long_name_variable_4 -``` - -上の3つのコードはすべて同じ意味です。このスタイルは`if`関数などを使用するときにも便利です。 +上の2つのコードは同じ意味です。このスタイルは`if`関数などを使用するときにも便利です。 ```python result = if Bool.sample!(): @@ -52,7 +46,15 @@ result = if Bool.sample!(): 0 ``` -`:`の後はコメント以外のコードを書いてはならず、必ず改行しなくてはなりません。 +この場合、`:`の後はコメント以外のコードを書いてはならず、必ず改行しなくてはなりません。 +また、関数の直後に`:`を使うことはできません。これができるのは`do`と`do!`のみです。 + +```python +# NG +f: + x + y +``` ## キーワード引数(Keyword Arguments) diff --git a/examples/control.er b/examples/control.er index 00dd8561..7fc6d0a7 100644 --- a/examples/control.er +++ b/examples/control.er @@ -4,25 +4,21 @@ s = if cond: do "else block" assert s == "then block" -# else: binary operator -x = cond.then 1 else 2 -assert x == 1 - if! cond: do!: - print! "then block" + print! "then block!" do!: - print! "else block" + print! "else block!" a = [1, 2, 3] sum = match a: [x, y, z] -> x + y + z (x, y, z) -> x + y + z {x; y; z} -> x + y + z - i: Int -> i + (i: Int) -> i _ -> panic "unknown object" -for! 0.., i => +for! 0..1000, i => print! "i = {i}" if i >= 100: do return break() diff --git a/examples/enum.er b/examples/enum.er index 84036916..bba146a9 100644 --- a/examples/enum.er +++ b/examples/enum.er @@ -1,26 +1,25 @@ LitExpr = Class {.i = Int}, Impl := Show LitExpr. new i = Self::__new__ {.i;} - show &self = "{self.i}" + show self = "{self.i}" AddExpr = Class {.lhs = Expr, .rhs = Expr}, Impl := Show AddExpr. new lhs, rhs = Self::__new__ {.lhs; .rhs} - show &self = "{self.lhs} + {self.rhs}" + show self = "{self.lhs} + {self.rhs}" SubExpr = Class {.lhs = Expr, .rhs = Expr}, Impl := Show SubExpr. new lhs, rhs = Self::__new__ {.lhs; .rhs} - show &self = "{self.lhs} - {self.rhs}" + show self = "{self.lhs} - {self.rhs}" PosExpr = Class {.expr = Expr}, Impl := Show PosExpr. new expr = Self::__new__ {.expr;} - show &self = "+{self.expr}" + show self = "+{self.expr}" NegExpr = Class {.expr = Expr}, Impl := Show NegExpr. new expr = Self::__new__ {.expr;} - show &self = "-{self.expr}" + show self = "-{self.expr}" -Expr = Enum: - LitExpr +Expr = Enum LitExpr: AddExpr SubExpr NegExpr @@ -29,11 +28,11 @@ Expr. add = Self.cons(AddExpr) eval self = match self: - l: Expr.LitExpr -> l.i - a: Expr.AddExpr -> a.lhs + a.rhs - s: Expr.SubExpr -> s.lhs - s.rhs - p: Expr.PosExpr -> p.expr - n: Expr.NegExpr -> -n.expr + (l: Expr.LitExpr) -> l.i + (a: Expr.AddExpr) -> a.lhs + a.rhs + (s: Expr.SubExpr) -> s.lhs - s.rhs + (p: Expr.PosExpr) -> p.expr + (n: Expr.NegExpr) -> -n.expr expr = Expr.add Expr.lit(1), Expr.lit(2) print! expr # 1 + 2 diff --git a/src/dummy.rs b/src/dummy.rs index bbf9e60f..bac2cb1f 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -6,12 +6,14 @@ use std::time::Duration; use erg_common::config::{ErgConfig, Input}; use erg_common::python_util::{exec_py, exec_pyc}; -use erg_common::str::Str; use erg_common::traits::Runnable; use erg_compiler::error::{CompileError, CompileErrors}; use erg_compiler::Compiler; +pub type EvalError = CompileError; +pub type EvalErrors = CompileErrors; + /// Open the Python interpreter as a server and act as an Erg interpreter by mediating communication /// /// Pythonインタープリタをサーバーとして開き、通信を仲介することでErgインタープリタとして振る舞う @@ -23,19 +25,23 @@ pub struct DummyVM { } impl Runnable for DummyVM { - type Err = CompileError; - type Errs = CompileErrors; + type Err = EvalError; + type Errs = EvalErrors; const NAME: &'static str = "Erg interpreter"; fn new(cfg: ErgConfig) -> Self { let stream = if cfg.input.is_repl() { - println!("Starting the REPL server..."); + if !cfg.quiet_startup { + println!("Starting the REPL server..."); + } let port = find_available_port(); let code = include_str!("scripts/repl_server.py") .replace("__PORT__", port.to_string().as_str()); exec_py(&code); let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, port); - println!("Connecting to the REPL server..."); + if !cfg.quiet_startup { + println!("Connecting to the REPL server..."); + } loop { match TcpStream::connect(&addr) { Ok(stream) => { @@ -45,7 +51,9 @@ impl Runnable for DummyVM { break Some(stream); } Err(_) => { - println!("Retrying to connect to the REPL server..."); + if !cfg.quiet_startup { + println!("Retrying to connect to the REPL server..."); + } sleep(Duration::from_millis(500)); continue; } @@ -97,7 +105,7 @@ impl Runnable for DummyVM { Ok(()) } - fn eval(&mut self, src: Str) -> Result { + fn eval(&mut self, src: String) -> Result { self.compiler .compile_and_dump_as_pyc(src, "o.pyc", "eval")?; let mut res = match self.stream.as_mut().unwrap().write("load".as_bytes()) { @@ -107,7 +115,7 @@ impl Runnable for DummyVM { Result::Ok(n) => { let s = std::str::from_utf8(&buf[..n]).unwrap(); if s == "[Exception] SystemExit" { - return Err(CompileErrors::from(CompileError::system_exit())); + return Err(EvalErrors::from(EvalError::system_exit())); } s.to_string() }