Merge branch 'main' into pr/320

This commit is contained in:
Shunsuke Shibayama 2023-06-09 22:04:44 +09:00
commit 924b22a171
91 changed files with 1836 additions and 1027 deletions

10
Cargo.lock generated
View file

@ -94,7 +94,7 @@ dependencies = [
[[package]] [[package]]
name = "els" name = "els"
version = "0.1.26-nightly.4" version = "0.1.27-nightly.0"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_compiler", "erg_compiler",
@ -105,7 +105,7 @@ dependencies = [
[[package]] [[package]]
name = "erg" name = "erg"
version = "0.6.14-nightly.4" version = "0.6.15-nightly.0"
dependencies = [ dependencies = [
"els", "els",
"erg_common", "erg_common",
@ -115,7 +115,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.14-nightly.4" version = "0.6.15-nightly.0"
dependencies = [ dependencies = [
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"crossterm", "crossterm",
@ -123,7 +123,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.14-nightly.4" version = "0.6.15-nightly.0"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -131,7 +131,7 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.14-nightly.4" version = "0.6.15-nightly.0"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"unicode-xid", "unicode-xid",

View file

@ -20,7 +20,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.6.14-nightly.4" version = "0.6.15-nightly.0"
authors = ["erg-lang team <moderation.erglang@gmail.com>"] authors = ["erg-lang team <moderation.erglang@gmail.com>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
@ -65,10 +65,10 @@ full = ["els", "full-repl", "unicode", "pretty"]
experimental = ["erg_common/experimental", "erg_parser/experimental", "erg_compiler/experimental"] experimental = ["erg_common/experimental", "erg_parser/experimental", "erg_compiler/experimental"]
[workspace.dependencies] [workspace.dependencies]
erg_common = { version = "0.6.14-nightly.4", path = "./crates/erg_common" } erg_common = { version = "0.6.15-nightly.0", path = "./crates/erg_common" }
erg_parser = { version = "0.6.14-nightly.4", path = "./crates/erg_parser" } erg_parser = { version = "0.6.15-nightly.0", path = "./crates/erg_parser" }
erg_compiler = { version = "0.6.14-nightly.4", path = "./crates/erg_compiler" } erg_compiler = { version = "0.6.15-nightly.0", path = "./crates/erg_compiler" }
els = { version = "0.1.26-nightly.4", path = "./crates/els" } els = { version = "0.1.27-nightly.0", path = "./crates/els" }
[dependencies] [dependencies]
erg_common = { workspace = true } erg_common = { workspace = true }

View file

@ -2,7 +2,7 @@
name = "els" name = "els"
description = "An Erg compiler frontend for IDEs, implements LSP." description = "An Erg compiler frontend for IDEs, implements LSP."
documentation = "http://docs.rs/els" documentation = "http://docs.rs/els"
version = "0.1.26-nightly.4" version = "0.1.27-nightly.0"
authors.workspace = true authors.workspace = true
license.workspace = true license.workspace = true
edition.workspace = true edition.workspace = true

View file

@ -3,10 +3,11 @@ use erg_compiler::erg_parser::parse::Parsable;
use lsp_types::CompletionResponse; use lsp_types::CompletionResponse;
use serde_json::Value; use serde_json::Value;
use erg_common::config::{ErgConfig, Input}; use erg_common::config::ErgConfig;
use erg_common::dict::Dict; use erg_common::dict::Dict;
use erg_common::env::erg_pystd_path; use erg_common::env::erg_pystd_path;
use erg_common::impl_u8_enum; use erg_common::impl_u8_enum;
use erg_common::io::Input;
use erg_common::python_util::BUILTIN_PYTHON_MODS; use erg_common::python_util::BUILTIN_PYTHON_MODS;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::shared::AtomicShared; use erg_common::shared::AtomicShared;

View file

@ -19,7 +19,7 @@ use crate::util::{self, NormalizedUrl};
impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> { impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
pub(crate) fn get_ast(&self, uri: &NormalizedUrl) -> Option<Module> { pub(crate) fn get_ast(&self, uri: &NormalizedUrl) -> Option<Module> {
let code = self.file_cache.get_entire_code(uri).ok()?; let code = self.file_cache.get_entire_code(uri).ok()?;
Parser::parse(code).ok() Parser::parse(code).ok().map(|artifact| artifact.ast)
} }
pub(crate) fn check_file<S: Into<String>>( pub(crate) fn check_file<S: Into<String>>(

View file

@ -294,9 +294,9 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
let src = self.file_cache.get_entire_code(&uri)?; let src = self.file_cache.get_entire_code(&uri)?;
let mut builder = ASTBuilder::new(self.cfg.inherit(path)); let mut builder = ASTBuilder::new(self.cfg.inherit(path));
let result = match builder.build_without_desugaring(src) { let result = match builder.build_without_desugaring(src) {
Ok(ast) => { Ok(artifact) => {
let mut state = ASTSemanticState::new(); let mut state = ASTSemanticState::new();
let tokens = state.enumerate_tokens(ast); let tokens = state.enumerate_tokens(artifact.ast);
Some(SemanticTokensResult::Tokens(tokens)) Some(SemanticTokensResult::Tokens(tokens))
} }
Err(_) => None, Err(_) => None,

View file

@ -2,24 +2,18 @@
//! //!
//! コマンドオプション(パーサー)を定義する //! コマンドオプション(パーサー)を定義する
use std::env; use std::env;
use std::ffi::OsStr;
use std::fmt; use std::fmt;
use std::fs::File; use std::io::{stdin, IsTerminal, Read};
use std::io::{stdin, BufRead, BufReader, IsTerminal, Read, Write}; use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process; use std::process;
use std::str::FromStr; use std::str::FromStr;
use crate::consts::{ERG_MODE, EXPERIMENTAL_MODE};
use crate::env::{erg_py_external_lib_path, erg_pystd_path, erg_std_path, python_site_packages};
use crate::help_messages::{command_message, mode_message, OPTIONS}; use crate::help_messages::{command_message, mode_message, OPTIONS};
use crate::io::{Input, Output};
use crate::levenshtein::get_similar_name; use crate::levenshtein::get_similar_name;
use crate::pathutil::add_postfix_foreach; use crate::normalize_path;
use crate::python_util::{detect_magic_number, get_python_version, get_sys_path, PythonVersion}; use crate::python_util::{detect_magic_number, get_python_version, PythonVersion};
use crate::random::random;
use crate::serialize::{get_magic_num_from_bytes, get_ver_from_magic_num}; use crate::serialize::{get_magic_num_from_bytes, get_ver_from_magic_num};
use crate::stdin::GLOBAL_STDIN;
use crate::{normalize_path, power_assert, read_file};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ErgMode { pub enum ErgMode {
@ -77,671 +71,6 @@ impl fmt::Display for ErgMode {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DummyStdin {
pub name: String,
current_line: usize,
lines: Vec<String>,
}
impl DummyStdin {
pub fn new(name: String, lines: Vec<String>) -> Self {
Self {
name,
current_line: 0,
lines,
}
}
pub fn read_line(&mut self) -> String {
let mut stdout = std::io::stdout();
if self.current_line >= self.lines.len() {
stdout.write_all("\n".as_bytes()).unwrap();
stdout.flush().unwrap();
// workaround: https://github.com/erg-lang/erg/issues/399
return "exit()".to_string();
}
let mut line = self.lines[self.current_line].clone();
self.current_line += 1;
line.push('\n');
stdout.write_all(line.as_bytes()).unwrap();
stdout.flush().unwrap();
line
}
pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec<String> {
self.lines[ln_begin - 1..=ln_end - 1].to_vec()
}
pub fn reread(&self) -> Option<String> {
self.lines.get(self.current_line).cloned()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InputKind {
File(PathBuf),
REPL,
DummyREPL(DummyStdin),
/// same content as cfg.command
Pipe(String),
/// from command option | eval
Str(String),
Dummy,
}
impl InputKind {
pub const fn is_repl(&self) -> bool {
matches!(self, Self::REPL | Self::DummyREPL(_))
}
pub fn path(&self) -> Option<&Path> {
match self {
Self::File(path) => Some(path),
_ => None,
}
}
pub fn enclosed_name(&self) -> &str {
match self {
Self::File(filename) => filename.to_str().unwrap_or("_"),
Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "<stdin>",
Self::Str(_) => "<string>",
Self::Dummy => "<dummy>",
}
}
pub fn dir(&self) -> PathBuf {
if let Self::File(path) = self {
let mut path = path.clone();
path.pop();
if path.parent().is_none() {
PathBuf::from(".")
} else {
path
}
} else {
PathBuf::from(".")
}
}
pub fn project_root(&self) -> Option<PathBuf> {
if let Self::File(path) = self {
let mut parent = path.clone();
while parent.pop() {
if parent.join("package.er").exists() {
return Some(parent);
}
}
None
} else {
None
}
}
}
/// Since input is not always only from files
/// Unify operations with `Input`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Input {
pub(crate) kind: InputKind,
/// Unique id to avoid file name collision
id: u64,
}
impl From<PathBuf> for Input {
fn from(path: PathBuf) -> Self {
Self::file(path)
}
}
impl From<&Path> for Input {
fn from(path: &Path) -> Self {
Self::file(path.to_path_buf())
}
}
impl Input {
pub const fn new(kind: InputKind, id: u64) -> Self {
Self { kind, id }
}
pub fn file(path: PathBuf) -> Self {
Self::new(InputKind::File(path), random())
}
pub fn pipe(src: String) -> Self {
Self::new(InputKind::Pipe(src), random())
}
pub fn str(src: String) -> Self {
Self::new(InputKind::Str(src), random())
}
pub fn repl() -> Self {
Self::new(InputKind::REPL, random())
}
pub fn dummy() -> Self {
Self::new(InputKind::Dummy, random())
}
pub fn dummy_repl(stdin: DummyStdin) -> Self {
Self::new(InputKind::DummyREPL(stdin), random())
}
pub const fn is_repl(&self) -> bool {
self.kind.is_repl()
}
pub const fn id(&self) -> u64 {
self.id
}
pub fn path(&self) -> Option<&Path> {
self.kind.path()
}
pub fn dir(&self) -> PathBuf {
self.kind.dir()
}
pub fn project_root(&self) -> Option<PathBuf> {
self.kind.project_root()
}
pub fn enclosed_name(&self) -> &str {
self.kind.enclosed_name()
}
pub fn lineno(&self) -> usize {
GLOBAL_STDIN.lineno()
}
pub fn block_begin(&self) -> usize {
GLOBAL_STDIN.block_begin()
}
pub fn set_block_begin(&self) {
GLOBAL_STDIN.set_block_begin(self.lineno())
}
pub fn insert_whitespace(&self, whitespace: &str) {
GLOBAL_STDIN.insert_whitespace(whitespace);
}
pub fn set_indent(&self, indent: usize) {
GLOBAL_STDIN.set_indent(indent);
}
pub fn file_stem(&self) -> String {
match &self.kind {
InputKind::File(filename) => format!(
"{}_{}",
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_"),
self.id
),
InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id),
InputKind::DummyREPL(stdin) => format!("stdin_{}_{}", stdin.name, self.id),
InputKind::Str(_) => format!("string_{}", self.id),
InputKind::Dummy => "dummy".to_string(),
}
}
pub fn full_path(&self) -> PathBuf {
match &self.kind {
InputKind::File(filename) => {
PathBuf::from(format!("{}_{}", filename.display(), self.id))
}
_ => PathBuf::from(self.file_stem()),
}
}
pub fn filename(&self) -> String {
match &self.kind {
InputKind::File(filename) => format!(
"{}_{}",
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_"),
self.id
),
_ => self.file_stem(),
}
}
pub fn unescaped_file_stem(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
InputKind::Dummy => "dummy",
}
}
pub fn unescaped_filename(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
InputKind::Dummy => "dummy",
}
}
pub fn unescaped_path(&self) -> &Path {
match &self.kind {
InputKind::File(filename) => filename.as_path(),
InputKind::REPL | InputKind::Pipe(_) => Path::new("stdin"),
InputKind::DummyREPL(_stdin) => Path::new("stdin"),
InputKind::Str(_) => Path::new("string"),
InputKind::Dummy => Path::new("dummy"),
}
}
pub fn module_name(&self) -> String {
match &self.kind {
InputKind::File(filename) => {
let file_stem = if filename.file_stem() == Some(OsStr::new("__init__")) {
filename.parent().and_then(|p| p.file_stem())
} else {
filename.file_stem()
};
file_stem
.and_then(|f| f.to_str())
.unwrap_or("_")
.to_string()
}
InputKind::REPL | InputKind::Pipe(_) => "<stdin>".to_string(),
InputKind::DummyREPL(stdin) => stdin.name.clone(),
InputKind::Str(_) => "<string>".to_string(),
InputKind::Dummy => "<dummy>".to_string(),
}
}
pub fn read(&mut self) -> String {
match &mut self.kind {
InputKind::File(filename) => {
let file = match File::open(&filename) {
Ok(f) => f,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
let lossy = filename.to_str().unwrap().to_string();
println!("cannot open '{lossy}': [Errno {code}] {e}",);
process::exit(code);
}
};
match read_file(file) {
Ok(s) => s,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!(
"cannot read '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
}
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.read(),
InputKind::DummyREPL(dummy) => dummy.read_line(),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn try_read(&mut self) -> std::io::Result<String> {
match &mut self.kind {
InputKind::File(filename) => {
let file = File::open(filename)?;
read_file(file)
}
InputKind::Pipe(s) | InputKind::Str(s) => Ok(s.clone()),
InputKind::REPL => Ok(GLOBAL_STDIN.read()),
InputKind::DummyREPL(dummy) => Ok(dummy.read_line()),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn read_non_dummy(&self) -> String {
match &self.kind {
InputKind::File(filename) => {
let file = match File::open(filename) {
Ok(f) => f,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
let lossy = filename.to_str().unwrap().to_string();
println!("cannot open '{lossy}': [Errno {code}] {e}",);
process::exit(code);
}
};
match read_file(file) {
Ok(s) => s,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!(
"cannot read '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
}
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.read(),
InputKind::Dummy | InputKind::DummyREPL(_) => panic!("cannot read from a dummy file"),
}
}
pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec<String> {
power_assert!(ln_begin, >=, 1);
match &self.kind {
InputKind::File(filename) => match File::open(filename) {
Ok(file) => {
let mut codes = vec![];
let mut lines = BufReader::new(file).lines().skip(ln_begin - 1);
for _ in ln_begin..=ln_end {
codes.push(lines.next().unwrap_or_else(|| Ok("".to_string())).unwrap());
}
codes
}
Err(_) => vec!["<file not found>".into()],
},
InputKind::Pipe(s) | InputKind::Str(s) => s.split('\n').collect::<Vec<_>>()
[ln_begin - 1..=ln_end - 1]
.iter()
.map(|s| s.to_string())
.collect(),
InputKind::REPL => {
if ln_begin == ln_end {
vec![GLOBAL_STDIN.reread()]
} else {
GLOBAL_STDIN.reread_lines(ln_begin, ln_end)
}
}
InputKind::DummyREPL(dummy) => dummy.reread_lines(ln_begin, ln_end),
InputKind::Dummy => panic!("cannot read lines from a dummy file"),
}
}
pub fn reread(&self) -> String {
match &self.kind {
InputKind::File(path) => {
let mut reader = BufReader::new(File::open(path).unwrap());
let mut buf = String::new();
reader.read_to_string(&mut buf).unwrap();
buf
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.reread().trim_end().to_owned(),
InputKind::DummyREPL(dummy) => dummy.reread().unwrap_or_default(),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn sys_path(&self) -> Result<Vec<PathBuf>, std::io::Error> {
get_sys_path(self.unescaped_path().parent())
}
/// resolution order:
/// 1. `{path/to}.er`
/// 2. `{path/to}/__init__.er`
fn resolve_local(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut dir = self.dir();
dir.push(path);
dir.set_extension("er"); // {path/to}.er
let path = dir.canonicalize().or_else(|_| {
dir.pop(); // {path}
dir.push(path.iter().last().unwrap_or_default()); // {path/to}
dir.push("__init__.er"); // -> {path/to}/__init__.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
fn resolve_local_decl(&self, dir: PathBuf, path: &Path) -> Result<PathBuf, std::io::Error> {
self._resolve_local_decl(dir.clone(), path).or_else(|_| {
let path = add_postfix_foreach(path, ".d");
self._resolve_local_decl(dir, &path)
})
}
/// resolution order:
/// 1. `{path/to}.d.er`
/// 2. `{path/to}/__init__.d.er`
/// 3. `{path}/__pycache__/{to}.d.er`
/// 4. `{path/to}/__pycache__/__init__.d.er`
fn _resolve_local_decl(
&self,
mut dir: PathBuf,
path: &Path,
) -> Result<PathBuf, std::io::Error> {
let mut comps = path.components();
let last = comps
.next_back()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "path is empty"))?;
let last_path = Path::new(&last);
dir.push(comps);
dir.push(last_path);
dir.set_extension("d.er"); // {path/to}.d.er
let path = dir
.canonicalize()
.or_else(|_| {
dir.pop(); // {path/to}.d.er -> {path}
dir.push(last_path); // -> {path/to}
dir.push("__init__.d.er"); // -> {path/to}/__init__.d.er
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // -> {path/to}
dir.pop(); // -> {path}
dir.push("__pycache__"); // -> {path}/__pycache__
dir.push(last_path); // -> {path}/__pycache__/{to}
dir.set_extension("d.er"); // -> {path}/__pycache__/{to}.d.er
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // -> {path}/__pycache__
dir.pop(); // -> {path}
dir.push(last_path); // -> {path/to}
dir.push("__pycache__"); // -> {path/to}/__pycache__
dir.push("__init__.d.er"); // -> {path/to}/__pycache__/__init__.d.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
fn resolve_local_py(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut dir = self.dir();
dir.push(path);
dir.set_extension("py");
let path = dir.canonicalize().or_else(|_| {
dir.pop();
dir.push(path);
dir.push("__init__.py"); // {path}/__init__.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
pub fn resolve_py(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
if ERG_MODE || path.starts_with("./") {
if let Ok(path) = self.resolve_local_py(path) {
return Ok(path);
}
}
for sys_path in self.sys_path()? {
let mut dir = sys_path;
dir.push(path);
dir.set_extension("py");
if dir.exists() {
return Ok(normalize_path(dir));
}
dir.pop();
dir.push(path);
dir.push("__init__.py");
if dir.exists() {
return Ok(normalize_path(dir));
}
if !EXPERIMENTAL_MODE {
break;
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("cannot find module `{}`", path.display()),
))
}
pub fn resolve_path(&self, path: &Path) -> Option<PathBuf> {
self.resolve_real_path(path)
.or_else(|| self.resolve_decl_path(path))
}
/// resolution order:
/// 1. `./{path/to}.er`
/// 2. `./{path/to}/__init__.er`
/// 3. `std/{path/to}.er`
/// 4. `std/{path/to}/__init__.er`
pub fn resolve_real_path(&self, path: &Path) -> Option<PathBuf> {
if let Ok(path) = self.resolve_local(path) {
Some(path)
} else if let Ok(path) = erg_std_path()
.join(format!("{}.er", path.display()))
.canonicalize()
{
Some(normalize_path(path))
} else if let Ok(path) = erg_std_path()
.join(format!("{}", path.display()))
.join("__init__.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
/// resolution order:
/// 1. `{path/to}.d.er`
/// 2. `{path/to}/__init__.d.er`
/// 3. `{path}/__pycache__/{to}.d.er`
/// 4. `{path/to}/__pycache__/__init__.d.er`
/// 5. `{path.d/to.d}/__init__.d.er`
/// 6. `{path.d/to.d}/__pycache__/__init__.d.er`
/// (and repeat for the project root)
/// 7. `std/{path/to}.d.er`
/// 8. `std/{path/to}/__init__.d.er`
/// 9. `site-packages/{path}/__pycache__/{to}.d.er`
/// 10. `site-packages/{path/to}/__pycache__/__init__.d.er`
pub fn resolve_decl_path(&self, path: &Path) -> Option<PathBuf> {
if let Ok(path) = self.resolve_local_decl(self.dir(), path) {
return Some(path);
}
// e.g. root: lib/external/pandas.d, path: pandas/core/frame
if let Some(dir) = self.project_root().as_ref().and_then(|root| root.parent()) {
if let Ok(path) = self.resolve_local_decl(dir.to_path_buf(), path) {
return Some(path);
}
}
let py_roots = [erg_pystd_path, erg_py_external_lib_path];
for root in py_roots {
if let Some(path) = Self::resolve_std_decl_path(root(), path) {
return Some(path);
}
}
for site_packages in python_site_packages() {
if let Some(path) = Self::resolve_site_pkgs_decl_path(site_packages, path) {
return Some(path);
}
}
None
}
/// 1. `site-packages/{path/to}.d.er`
/// 2. `site-packages/{path.d/to.d}/__init__.d.er`
fn resolve_std_decl_path(root: PathBuf, path: &Path) -> Option<PathBuf> {
let mut path = add_postfix_foreach(path, ".d");
path.set_extension("d.er"); // set_extension overrides the previous one
if let Ok(path) = root.join(&path).canonicalize() {
Some(normalize_path(path))
// d.er -> .d
} else if let Ok(path) = root
.join({
path.set_extension("");
path
})
.join("__init__.d.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
/// 1. `site-packages/__pycache__/{path/to}.d.er`
/// 2. `site-packages/{path/to}/__pycache__/__init__.d.er`
///
/// e.g. `toml/encoder`
/// -> `site-packages/toml/__pycache__/encoder.d.er`, `site-packages/toml/encoder/__pycache__/__init__.d.er`
fn resolve_site_pkgs_decl_path(site_packages: PathBuf, path: &Path) -> Option<PathBuf> {
let dir = path.parent().unwrap_or_else(|| Path::new(""));
let mut file_path = PathBuf::from(path.file_stem().unwrap_or_default());
file_path.set_extension("d.er"); // set_extension overrides the previous one
if let Ok(path) = site_packages
.join(dir)
.join("__pycache__")
.join(&file_path)
.canonicalize()
{
Some(normalize_path(path))
} else if let Ok(path) = site_packages
.join(path)
.join("__pycache__")
.join("__init__.d.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
pub fn try_push_path(mut path: PathBuf, add: &Path) -> Result<PathBuf, String> {
path.pop(); // __init__.d.er
if let Ok(path) = path.join(add).canonicalize() {
Ok(normalize_path(path))
} else if let Ok(path) = path.join(format!("{}.d.er", add.display())).canonicalize() {
Ok(normalize_path(path))
} else if let Ok(path) = path
.join(format!("{}.d", add.display()))
.join("__init__.d.er")
.canonicalize()
{
Ok(normalize_path(path))
} else {
Err(format!("{} // {}", path.display(), add.display()))
}
}
pub fn decl_file_is(&self, decl_path: &Path) -> bool {
let mut py_path = self.unescaped_path().to_path_buf();
py_path.set_extension("d.er");
if decl_path == py_path {
return true;
}
let last = py_path.file_name().unwrap_or_default().to_os_string();
py_path.pop();
py_path.push("__pycache__");
py_path.push(last);
decl_path == py_path
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ErgConfig { pub struct ErgConfig {
pub mode: ErgMode, pub mode: ErgMode,
@ -759,7 +88,8 @@ pub struct ErgConfig {
pub quiet_repl: bool, pub quiet_repl: bool,
pub show_type: bool, pub show_type: bool,
pub input: Input, pub input: Input,
pub output_dir: Option<&'static str>, pub output: Output,
pub dist_dir: Option<&'static str>,
/// module name to be executed /// module name to be executed
pub module: &'static str, pub module: &'static str,
/// verbosity level for system messages. /// verbosity level for system messages.
@ -786,7 +116,8 @@ impl Default for ErgConfig {
quiet_repl: false, quiet_repl: false,
show_type: false, show_type: false,
input: Input::repl(), input: Input::repl(),
output_dir: None, output: Output::stdout(),
dist_dir: None,
module: "<module>", module: "<module>",
verbose: 1, verbose: 1,
ps1: ">>> ", ps1: ">>> ",
@ -820,7 +151,7 @@ impl ErgConfig {
} }
pub fn dump_path(&self) -> PathBuf { pub fn dump_path(&self) -> PathBuf {
if let Some(output) = &self.output_dir { if let Some(output) = &self.dist_dir {
PathBuf::from(format!("{output}/{}", self.input.filename())) PathBuf::from(format!("{output}/{}", self.input.filename()))
} else { } else {
self.input.full_path() self.input.full_path()
@ -828,7 +159,7 @@ impl ErgConfig {
} }
pub fn dump_filename(&self) -> String { pub fn dump_filename(&self) -> String {
if let Some(output) = &self.output_dir { if let Some(output) = &self.dist_dir {
format!("{output}/{}", self.input.filename()) format!("{output}/{}", self.input.filename())
} else { } else {
self.input.filename() self.input.filename()
@ -948,7 +279,7 @@ impl ErgConfig {
.next() .next()
.expect("the value of `--output-dir` is not passed") .expect("the value of `--output-dir` is not passed")
.into_boxed_str(); .into_boxed_str();
cfg.output_dir = Some(Box::leak(output_dir)); cfg.dist_dir = Some(Box::leak(output_dir));
} }
"--py-command" | "--python-command" => { "--py-command" | "--python-command" => {
let py_command = args let py_command = args

View file

@ -5,7 +5,7 @@ use std::cmp::{self, Ordering};
use std::fmt; use std::fmt;
use std::io::{stderr, BufWriter, Write as _}; use std::io::{stderr, BufWriter, Write as _};
use crate::config::{Input, InputKind}; use crate::io::{Input, InputKind};
use crate::style::Attribute; use crate::style::Attribute;
use crate::style::Characters; use crate::style::Characters;
use crate::style::Color; use crate::style::Color;
@ -985,12 +985,18 @@ macro_rules! impl_display_and_error {
} }
pub trait MultiErrorDisplay<Item: ErrorDisplay>: Stream<Item> { pub trait MultiErrorDisplay<Item: ErrorDisplay>: Stream<Item> {
fn fmt_all_stderr(&self) { fn write_all_stderr(&self) {
for err in self.iter() { for err in self.iter() {
err.write_to_stderr(); err.write_to_stderr();
} }
} }
fn write_all_to(&self, w: &mut impl std::io::Write) {
for err in self.iter() {
err.write_to(w);
}
}
fn fmt_all(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt_all(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for err in self.iter() { for err in self.iter() {
err.format(f)?; err.format(f)?;

739
crates/erg_common/io.rs Normal file
View file

@ -0,0 +1,739 @@
use std::ffi::OsStr;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, Stdout, Write};
use std::path::{Path, PathBuf};
use std::process;
use std::process::Stdio;
use crate::consts::{ERG_MODE, EXPERIMENTAL_MODE};
use crate::env::{erg_py_external_lib_path, erg_pystd_path, erg_std_path, python_site_packages};
use crate::pathutil::add_postfix_foreach;
use crate::python_util::get_sys_path;
use crate::random::random;
use crate::stdin::GLOBAL_STDIN;
use crate::{normalize_path, power_assert, read_file};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DummyStdin {
pub name: String,
current_line: usize,
lines: Vec<String>,
}
impl DummyStdin {
pub fn new(name: String, lines: Vec<String>) -> Self {
Self {
name,
current_line: 0,
lines,
}
}
pub fn read_line(&mut self) -> String {
let mut stdout = std::io::stdout();
if self.current_line >= self.lines.len() {
stdout.write_all("\n".as_bytes()).unwrap();
stdout.flush().unwrap();
// workaround: https://github.com/erg-lang/erg/issues/399
return "exit()".to_string();
}
let mut line = self.lines[self.current_line].clone();
self.current_line += 1;
line.push('\n');
stdout.write_all(line.as_bytes()).unwrap();
stdout.flush().unwrap();
line
}
pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec<String> {
self.lines[ln_begin - 1..=ln_end - 1].to_vec()
}
pub fn reread(&self) -> Option<String> {
self.lines.get(self.current_line).cloned()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum InputKind {
File(PathBuf),
REPL,
DummyREPL(DummyStdin),
/// same content as cfg.command
Pipe(String),
/// from command option | eval
Str(String),
Dummy,
}
impl InputKind {
pub const fn is_repl(&self) -> bool {
matches!(self, Self::REPL | Self::DummyREPL(_))
}
pub fn path(&self) -> Option<&Path> {
match self {
Self::File(path) => Some(path),
_ => None,
}
}
pub fn enclosed_name(&self) -> &str {
match self {
Self::File(filename) => filename.to_str().unwrap_or("_"),
Self::REPL | Self::DummyREPL(_) | Self::Pipe(_) => "<stdin>",
Self::Str(_) => "<string>",
Self::Dummy => "<dummy>",
}
}
pub fn dir(&self) -> PathBuf {
if let Self::File(path) = self {
let mut path = path.clone();
path.pop();
if path.parent().is_none() {
PathBuf::from(".")
} else {
path
}
} else {
PathBuf::from(".")
}
}
pub fn project_root(&self) -> Option<PathBuf> {
if let Self::File(path) = self {
let mut parent = path.clone();
while parent.pop() {
if parent.join("package.er").exists() {
return Some(parent);
}
}
None
} else {
None
}
}
}
/// Since input is not always only from files
/// Unify operations with `Input`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Input {
pub(crate) kind: InputKind,
/// Unique id to avoid file name collision
id: u64,
}
impl From<PathBuf> for Input {
fn from(path: PathBuf) -> Self {
Self::file(path)
}
}
impl From<&Path> for Input {
fn from(path: &Path) -> Self {
Self::file(path.to_path_buf())
}
}
impl Input {
pub const fn new(kind: InputKind, id: u64) -> Self {
Self { kind, id }
}
pub fn file(path: PathBuf) -> Self {
Self::new(InputKind::File(path), random())
}
pub fn pipe(src: String) -> Self {
Self::new(InputKind::Pipe(src), random())
}
pub fn str(src: String) -> Self {
Self::new(InputKind::Str(src), random())
}
pub fn repl() -> Self {
Self::new(InputKind::REPL, random())
}
pub fn dummy() -> Self {
Self::new(InputKind::Dummy, random())
}
pub fn dummy_repl(stdin: DummyStdin) -> Self {
Self::new(InputKind::DummyREPL(stdin), random())
}
pub const fn is_repl(&self) -> bool {
self.kind.is_repl()
}
pub const fn id(&self) -> u64 {
self.id
}
pub fn path(&self) -> Option<&Path> {
self.kind.path()
}
pub fn dir(&self) -> PathBuf {
self.kind.dir()
}
pub fn project_root(&self) -> Option<PathBuf> {
self.kind.project_root()
}
pub fn enclosed_name(&self) -> &str {
self.kind.enclosed_name()
}
pub fn lineno(&self) -> usize {
GLOBAL_STDIN.lineno()
}
pub fn block_begin(&self) -> usize {
GLOBAL_STDIN.block_begin()
}
pub fn set_block_begin(&self) {
GLOBAL_STDIN.set_block_begin(self.lineno())
}
pub fn insert_whitespace(&self, whitespace: &str) {
GLOBAL_STDIN.insert_whitespace(whitespace);
}
pub fn set_indent(&self, indent: usize) {
GLOBAL_STDIN.set_indent(indent);
}
pub fn file_stem(&self) -> String {
match &self.kind {
InputKind::File(filename) => format!(
"{}_{}",
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_"),
self.id
),
InputKind::REPL | InputKind::Pipe(_) => format!("stdin_{}", self.id),
InputKind::DummyREPL(stdin) => format!("stdin_{}_{}", stdin.name, self.id),
InputKind::Str(_) => format!("string_{}", self.id),
InputKind::Dummy => "dummy".to_string(),
}
}
pub fn full_path(&self) -> PathBuf {
match &self.kind {
InputKind::File(filename) => {
PathBuf::from(format!("{}_{}", filename.display(), self.id))
}
_ => PathBuf::from(self.file_stem()),
}
}
pub fn filename(&self) -> String {
match &self.kind {
InputKind::File(filename) => format!(
"{}_{}",
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_"),
self.id
),
_ => self.file_stem(),
}
}
pub fn unescaped_file_stem(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_stem().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
InputKind::Dummy => "dummy",
}
}
pub fn unescaped_filename(&self) -> &str {
match &self.kind {
InputKind::File(filename) => {
filename.file_name().and_then(|f| f.to_str()).unwrap_or("_")
}
InputKind::REPL | InputKind::Pipe(_) => "stdin",
InputKind::DummyREPL(_stdin) => "stdin",
InputKind::Str(_) => "string",
InputKind::Dummy => "dummy",
}
}
pub fn unescaped_path(&self) -> &Path {
match &self.kind {
InputKind::File(filename) => filename.as_path(),
InputKind::REPL | InputKind::Pipe(_) => Path::new("stdin"),
InputKind::DummyREPL(_stdin) => Path::new("stdin"),
InputKind::Str(_) => Path::new("string"),
InputKind::Dummy => Path::new("dummy"),
}
}
pub fn module_name(&self) -> String {
match &self.kind {
InputKind::File(filename) => {
let file_stem = if filename.file_stem() == Some(OsStr::new("__init__")) {
filename.parent().and_then(|p| p.file_stem())
} else {
filename.file_stem()
};
file_stem
.and_then(|f| f.to_str())
.unwrap_or("_")
.to_string()
}
InputKind::REPL | InputKind::Pipe(_) => "<stdin>".to_string(),
InputKind::DummyREPL(stdin) => stdin.name.clone(),
InputKind::Str(_) => "<string>".to_string(),
InputKind::Dummy => "<dummy>".to_string(),
}
}
pub fn read(&mut self) -> String {
match &mut self.kind {
InputKind::File(filename) => {
let file = match File::open(&filename) {
Ok(f) => f,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
let lossy = filename.to_str().unwrap().to_string();
println!("cannot open '{lossy}': [Errno {code}] {e}",);
process::exit(code);
}
};
match read_file(file) {
Ok(s) => s,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!(
"cannot read '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
}
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.read(),
InputKind::DummyREPL(dummy) => dummy.read_line(),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn try_read(&mut self) -> std::io::Result<String> {
match &mut self.kind {
InputKind::File(filename) => {
let file = File::open(filename)?;
read_file(file)
}
InputKind::Pipe(s) | InputKind::Str(s) => Ok(s.clone()),
InputKind::REPL => Ok(GLOBAL_STDIN.read()),
InputKind::DummyREPL(dummy) => Ok(dummy.read_line()),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn read_non_dummy(&self) -> String {
match &self.kind {
InputKind::File(filename) => {
let file = match File::open(filename) {
Ok(f) => f,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
let lossy = filename.to_str().unwrap().to_string();
println!("cannot open '{lossy}': [Errno {code}] {e}",);
process::exit(code);
}
};
match read_file(file) {
Ok(s) => s,
Err(e) => {
let code = e.raw_os_error().unwrap_or(1);
println!(
"cannot read '{}': [Errno {code}] {e}",
filename.to_string_lossy()
);
process::exit(code);
}
}
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.read(),
InputKind::Dummy | InputKind::DummyREPL(_) => panic!("cannot read from a dummy file"),
}
}
pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec<String> {
power_assert!(ln_begin, >=, 1);
match &self.kind {
InputKind::File(filename) => match File::open(filename) {
Ok(file) => {
let mut codes = vec![];
let mut lines = BufReader::new(file).lines().skip(ln_begin - 1);
for _ in ln_begin..=ln_end {
codes.push(lines.next().unwrap_or_else(|| Ok("".to_string())).unwrap());
}
codes
}
Err(_) => vec!["<file not found>".into()],
},
InputKind::Pipe(s) | InputKind::Str(s) => s.split('\n').collect::<Vec<_>>()
[ln_begin - 1..=ln_end - 1]
.iter()
.map(|s| s.to_string())
.collect(),
InputKind::REPL => {
if ln_begin == ln_end {
vec![GLOBAL_STDIN.reread()]
} else {
GLOBAL_STDIN.reread_lines(ln_begin, ln_end)
}
}
InputKind::DummyREPL(dummy) => dummy.reread_lines(ln_begin, ln_end),
InputKind::Dummy => panic!("cannot read lines from a dummy file"),
}
}
pub fn reread(&self) -> String {
match &self.kind {
InputKind::File(path) => {
let mut reader = BufReader::new(File::open(path).unwrap());
let mut buf = String::new();
reader.read_to_string(&mut buf).unwrap();
buf
}
InputKind::Pipe(s) | InputKind::Str(s) => s.clone(),
InputKind::REPL => GLOBAL_STDIN.reread().trim_end().to_owned(),
InputKind::DummyREPL(dummy) => dummy.reread().unwrap_or_default(),
InputKind::Dummy => panic!("cannot read from a dummy file"),
}
}
pub fn sys_path(&self) -> Result<Vec<PathBuf>, std::io::Error> {
get_sys_path(self.unescaped_path().parent())
}
/// resolution order:
/// 1. `{path/to}.er`
/// 2. `{path/to}/__init__.er`
fn resolve_local(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut dir = self.dir();
dir.push(path);
dir.set_extension("er"); // {path/to}.er
let path = dir.canonicalize().or_else(|_| {
dir.pop(); // {path}
dir.push(path.iter().last().unwrap_or_default()); // {path/to}
dir.push("__init__.er"); // -> {path/to}/__init__.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
fn resolve_local_decl(&self, dir: PathBuf, path: &Path) -> Result<PathBuf, std::io::Error> {
self._resolve_local_decl(dir.clone(), path).or_else(|_| {
let path = add_postfix_foreach(path, ".d");
self._resolve_local_decl(dir, &path)
})
}
/// resolution order:
/// 1. `{path/to}.d.er`
/// 2. `{path/to}/__init__.d.er`
/// 3. `{path}/__pycache__/{to}.d.er`
/// 4. `{path/to}/__pycache__/__init__.d.er`
fn _resolve_local_decl(
&self,
mut dir: PathBuf,
path: &Path,
) -> Result<PathBuf, std::io::Error> {
let mut comps = path.components();
let last = comps
.next_back()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "path is empty"))?;
let last_path = Path::new(&last);
dir.push(comps);
dir.push(last_path);
dir.set_extension("d.er"); // {path/to}.d.er
let path = dir
.canonicalize()
.or_else(|_| {
dir.pop(); // {path/to}.d.er -> {path}
dir.push(last_path); // -> {path/to}
dir.push("__init__.d.er"); // -> {path/to}/__init__.d.er
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // -> {path/to}
dir.pop(); // -> {path}
dir.push("__pycache__"); // -> {path}/__pycache__
dir.push(last_path); // -> {path}/__pycache__/{to}
dir.set_extension("d.er"); // -> {path}/__pycache__/{to}.d.er
dir.canonicalize()
})
.or_else(|_| {
dir.pop(); // -> {path}/__pycache__
dir.pop(); // -> {path}
dir.push(last_path); // -> {path/to}
dir.push("__pycache__"); // -> {path/to}/__pycache__
dir.push("__init__.d.er"); // -> {path/to}/__pycache__/__init__.d.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
fn resolve_local_py(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
let mut dir = self.dir();
dir.push(path);
dir.set_extension("py");
let path = dir.canonicalize().or_else(|_| {
dir.pop();
dir.push(path);
dir.push("__init__.py"); // {path}/__init__.er
dir.canonicalize()
})?;
Ok(normalize_path(path))
}
pub fn resolve_py(&self, path: &Path) -> Result<PathBuf, std::io::Error> {
if ERG_MODE || path.starts_with("./") {
if let Ok(path) = self.resolve_local_py(path) {
return Ok(path);
}
}
for sys_path in self.sys_path()? {
let mut dir = sys_path;
dir.push(path);
dir.set_extension("py");
if dir.exists() {
return Ok(normalize_path(dir));
}
dir.pop();
dir.push(path);
dir.push("__init__.py");
if dir.exists() {
return Ok(normalize_path(dir));
}
if !EXPERIMENTAL_MODE {
break;
}
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("cannot find module `{}`", path.display()),
))
}
pub fn resolve_path(&self, path: &Path) -> Option<PathBuf> {
self.resolve_real_path(path)
.or_else(|| self.resolve_decl_path(path))
}
/// resolution order:
/// 1. `./{path/to}.er`
/// 2. `./{path/to}/__init__.er`
/// 3. `std/{path/to}.er`
/// 4. `std/{path/to}/__init__.er`
pub fn resolve_real_path(&self, path: &Path) -> Option<PathBuf> {
if let Ok(path) = self.resolve_local(path) {
Some(path)
} else if let Ok(path) = erg_std_path()
.join(format!("{}.er", path.display()))
.canonicalize()
{
Some(normalize_path(path))
} else if let Ok(path) = erg_std_path()
.join(format!("{}", path.display()))
.join("__init__.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
/// resolution order:
/// 1. `{path/to}.d.er`
/// 2. `{path/to}/__init__.d.er`
/// 3. `{path}/__pycache__/{to}.d.er`
/// 4. `{path/to}/__pycache__/__init__.d.er`
/// 5. `{path.d/to.d}/__init__.d.er`
/// 6. `{path.d/to.d}/__pycache__/__init__.d.er`
/// (and repeat for the project root)
/// 7. `std/{path/to}.d.er`
/// 8. `std/{path/to}/__init__.d.er`
/// 9. `site-packages/{path}/__pycache__/{to}.d.er`
/// 10. `site-packages/{path/to}/__pycache__/__init__.d.er`
pub fn resolve_decl_path(&self, path: &Path) -> Option<PathBuf> {
if let Ok(path) = self.resolve_local_decl(self.dir(), path) {
return Some(path);
}
// e.g. root: lib/external/pandas.d, path: pandas/core/frame
if let Some(mut dir) = self.project_root() {
let mut path = path.iter().skip(1).collect::<PathBuf>();
if path == Path::new("") {
path.extend(dir.iter().last());
dir.pop();
}
if let Ok(path) = self.resolve_local_decl(dir, &path) {
return Some(path);
}
}
let py_roots = [erg_pystd_path, erg_py_external_lib_path];
for root in py_roots {
if let Some(path) = Self::resolve_std_decl_path(root(), path) {
return Some(path);
}
}
for site_packages in python_site_packages() {
if let Some(path) = Self::resolve_site_pkgs_decl_path(site_packages, path) {
return Some(path);
}
}
None
}
/// 1. `site-packages/{path/to}.d.er`
/// 2. `site-packages/{path.d/to.d}/__init__.d.er`
fn resolve_std_decl_path(root: PathBuf, path: &Path) -> Option<PathBuf> {
let mut path = add_postfix_foreach(path, ".d");
path.set_extension("d.er"); // set_extension overrides the previous one
if let Ok(path) = root.join(&path).canonicalize() {
Some(normalize_path(path))
// d.er -> .d
} else if let Ok(path) = root
.join({
path.set_extension("");
path
})
.join("__init__.d.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
/// 1. `site-packages/__pycache__/{path/to}.d.er`
/// 2. `site-packages/{path/to}/__pycache__/__init__.d.er`
///
/// e.g. `toml/encoder`
/// -> `site-packages/toml/__pycache__/encoder.d.er`, `site-packages/toml/encoder/__pycache__/__init__.d.er`
fn resolve_site_pkgs_decl_path(site_packages: PathBuf, path: &Path) -> Option<PathBuf> {
let dir = path.parent().unwrap_or_else(|| Path::new(""));
let mut file_path = PathBuf::from(path.file_stem().unwrap_or_default());
file_path.set_extension("d.er"); // set_extension overrides the previous one
if let Ok(path) = site_packages
.join(dir)
.join("__pycache__")
.join(&file_path)
.canonicalize()
{
Some(normalize_path(path))
} else if let Ok(path) = site_packages
.join(path)
.join("__pycache__")
.join("__init__.d.er")
.canonicalize()
{
Some(normalize_path(path))
} else {
None
}
}
pub fn try_push_path(mut path: PathBuf, add: &Path) -> Result<PathBuf, String> {
path.pop(); // __init__.d.er
if let Ok(path) = path.join(add).canonicalize() {
Ok(normalize_path(path))
} else if let Ok(path) = path.join(format!("{}.d.er", add.display())).canonicalize() {
Ok(normalize_path(path))
} else if let Ok(path) = path
.join(format!("{}.d", add.display()))
.join("__init__.d.er")
.canonicalize()
{
Ok(normalize_path(path))
} else {
Err(format!("{} // {}", path.display(), add.display()))
}
}
pub fn decl_file_is(&self, decl_path: &Path) -> bool {
let mut py_path = self.unescaped_path().to_path_buf();
py_path.set_extension("d.er");
if decl_path == py_path {
return true;
}
let last = py_path.file_name().unwrap_or_default().to_os_string();
py_path.pop();
py_path.push("__pycache__");
py_path.push(last);
decl_path == py_path
}
}
#[derive(Debug)]
pub enum Output {
Stdout(Stdout),
File(File, String),
Null,
}
impl Clone for Output {
fn clone(&self) -> Self {
match self {
Self::Null => Self::Null,
Self::Stdout(_) => Self::stdout(),
Self::File(_, filename) => Self::file(filename.clone()),
}
}
}
impl std::io::Write for Output {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self {
Output::Stdout(stdout) => stdout.write(buf),
Output::File(file, _) => file.write(buf),
Output::Null => Ok(buf.len()),
}
}
fn flush(&mut self) -> std::io::Result<()> {
match self {
Output::Stdout(stdout) => stdout.flush(),
Output::File(file, _) => file.flush(),
Output::Null => Ok(()),
}
}
}
impl From<Output> for Stdio {
fn from(output: Output) -> Self {
match output {
Output::Stdout(_stdout) => Stdio::inherit(),
Output::File(file, _) => Stdio::from(file),
Output::Null => Stdio::null(),
}
}
}
impl Output {
pub fn stdout() -> Self {
Self::Stdout(std::io::stdout())
}
pub fn file(filename: String) -> Self {
Self::File(File::open(&filename).unwrap(), filename)
}
}

View file

@ -13,6 +13,7 @@ pub mod error;
pub mod fresh; pub mod fresh;
pub mod fxhash; pub mod fxhash;
pub mod help_messages; pub mod help_messages;
pub mod io;
pub mod lang; pub mod lang;
pub mod levenshtein; pub mod levenshtein;
pub mod macros; pub mod macros;

View file

@ -3,7 +3,7 @@
//! CPythonを呼び出すためのユーティリティー //! CPythonを呼び出すためのユーティリティー
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::Command; use std::process::{Command, Stdio};
use crate::fn_name_full; use crate::fn_name_full;
use crate::pathutil::remove_verbatim; use crate::pathutil::remove_verbatim;
@ -727,10 +727,11 @@ pub fn get_sys_path(working_dir: Option<&Path>) -> Result<Vec<PathBuf>, std::io:
} }
/// executes over a shell, cause `python` may not exist as an executable file (like pyenv) /// executes over a shell, cause `python` may not exist as an executable file (like pyenv)
pub fn exec_pyc<S: Into<String>>( pub fn exec_pyc<S: Into<String>, T: Into<Stdio>>(
file: S, file: S,
py_command: Option<&str>, py_command: Option<&str>,
argv: &[&'static str], argv: &[&'static str],
stdout: T,
) -> Option<i32> { ) -> Option<i32> {
let command = py_command let command = py_command
.map(ToString::to_string) .map(ToString::to_string)
@ -741,6 +742,7 @@ pub fn exec_pyc<S: Into<String>>(
.arg(command) .arg(command)
.arg(&file.into()) .arg(&file.into())
.args(argv) .args(argv)
.stdout(stdout)
.spawn() .spawn()
.expect("cannot execute python") .expect("cannot execute python")
} else { } else {
@ -748,6 +750,7 @@ pub fn exec_pyc<S: Into<String>>(
Command::new("sh") Command::new("sh")
.arg("-c") .arg("-c")
.arg(exec_command) .arg(exec_command)
.stdout(stdout)
.spawn() .spawn()
.expect("cannot execute python") .expect("cannot execute python")
}; };

View file

@ -6,7 +6,7 @@ use std::io::{stdin, BufRead, BufReader};
#[cfg(feature = "full-repl")] #[cfg(feature = "full-repl")]
use crossterm::{ use crossterm::{
cursor::{CursorShape, MoveToColumn, SetCursorShape}, cursor::MoveToColumn,
event::{read, Event, KeyCode, KeyEvent, KeyModifiers}, event::{read, Event, KeyCode, KeyEvent, KeyModifiers},
execute, execute,
style::Print, style::Print,
@ -103,7 +103,6 @@ impl StdinReader {
pub fn read(&mut self) -> String { pub fn read(&mut self) -> String {
enable_raw_mode().unwrap(); enable_raw_mode().unwrap();
let mut output = std::io::stdout(); let mut output = std::io::stdout();
execute!(output, SetCursorShape(CursorShape::Line)).unwrap();
let mut line = String::new(); let mut line = String::new();
self.input(&mut line).unwrap(); self.input(&mut line).unwrap();
disable_raw_mode().unwrap(); disable_raw_mode().unwrap();
@ -124,7 +123,8 @@ impl StdinReader {
{ {
consult_history = false; consult_history = false;
match (code, modifiers) { match (code, modifiers) {
(KeyCode::Char('z'), KeyModifiers::CONTROL) => { (KeyCode::Char('z'), KeyModifiers::CONTROL)
| (KeyCode::Char('d'), KeyModifiers::CONTROL) => {
println!(); println!();
line.clear(); line.clear();
line.push_str(":exit"); line.push_str(":exit");

View file

@ -9,9 +9,10 @@ use std::mem;
use std::process; use std::process;
use std::slice::{Iter, IterMut}; use std::slice::{Iter, IterMut};
use crate::config::{ErgConfig, Input, InputKind}; use crate::config::ErgConfig;
use crate::consts::{BUILD_DATE, GIT_HASH_SHORT, SEMVER}; use crate::consts::{BUILD_DATE, GIT_HASH_SHORT, SEMVER};
use crate::error::{ErrorDisplay, ErrorKind, Location, MultiErrorDisplay}; use crate::error::{ErrorDisplay, ErrorKind, Location, MultiErrorDisplay};
use crate::io::{Input, InputKind};
use crate::{addr_eq, chomp, log, switch_unreachable}; use crate::{addr_eq, chomp, log, switch_unreachable};
pub trait DequeStream<T>: Sized { pub trait DequeStream<T>: Sized {
@ -249,6 +250,10 @@ pub trait Stream<T>: Sized {
{ {
self.ref_mut_payload().extend(iter); self.ref_mut_payload().extend(iter);
} }
fn split_off(&mut self, at: usize) -> Vec<T> {
self.ref_mut_payload().split_off(at)
}
} }
#[macro_export] #[macro_export]
@ -792,7 +797,7 @@ pub trait Runnable: Sized + Default {
instance.quit_successfully(output); instance.quit_successfully(output);
} }
num_errors += errs.len(); num_errors += errs.len();
errs.fmt_all_stderr(); errs.write_all_stderr();
} }
} }
instance.input().set_block_begin(); instance.input().set_block_begin();
@ -916,7 +921,7 @@ pub trait Runnable: Sized + Default {
return ExitStatus::new(0, 0, num_errors); return ExitStatus::new(0, 0, num_errors);
} }
num_errors += errs.len(); num_errors += errs.len();
errs.fmt_all_stderr(); errs.write_all_stderr();
} }
} }
instance.input().set_block_begin(); instance.input().set_block_begin();
@ -931,7 +936,7 @@ pub trait Runnable: Sized + Default {
Ok(status) => status, Ok(status) => status,
Err(errs) => { Err(errs) => {
num_errors += errs.len(); num_errors += errs.len();
errs.fmt_all_stderr(); errs.write_all_stderr();
ExitStatus::new(1, 0, num_errors) ExitStatus::new(1, 0, num_errors)
} }
} }

View file

@ -10,7 +10,7 @@ use erg_parser::build_ast::ASTBuilder;
use crate::artifact::{BuildRunnable, Buildable, CompleteArtifact, IncompleteArtifact}; use crate::artifact::{BuildRunnable, Buildable, CompleteArtifact, IncompleteArtifact};
use crate::context::{Context, ContextProvider, ModuleContext}; use crate::context::{Context, ContextProvider, ModuleContext};
use crate::effectcheck::SideEffectChecker; use crate::effectcheck::SideEffectChecker;
use crate::error::{CompileError, CompileErrors}; use crate::error::{CompileError, CompileErrors, LowerWarnings};
use crate::lower::ASTLowerer; use crate::lower::ASTLowerer;
use crate::module::SharedCompilerResource; use crate::module::SharedCompilerResource;
use crate::ownercheck::OwnershipChecker; use crate::ownercheck::OwnershipChecker;
@ -66,18 +66,26 @@ impl Runnable for HIRBuilder {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> { fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy()); let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(self.cfg_mut().input.read())?; let artifact = builder
let artifact = self.check(ast, "exec").map_err(|arti| arti.errors)?; .build(self.cfg_mut().input.read())
artifact.warns.fmt_all_stderr(); .map_err(|arti| arti.errors)?;
artifact.warns.write_all_stderr();
let artifact = self
.check(artifact.ast, "exec")
.map_err(|arti| arti.errors)?;
artifact.warns.write_all_stderr();
println!("{}", artifact.object); println!("{}", artifact.object);
Ok(ExitStatus::compile_passed(artifact.warns.len())) Ok(ExitStatus::compile_passed(artifact.warns.len()))
} }
fn eval(&mut self, src: String) -> Result<String, Self::Errs> { fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy()); let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(src)?; let artifact = builder.build(src).map_err(|arti| arti.errors)?;
let artifact = self.check(ast, "eval").map_err(|arti| arti.errors)?; artifact.warns.write_all_stderr();
artifact.warns.fmt_all_stderr(); let artifact = self
.check(artifact.ast, "eval")
.map_err(|arti| arti.errors)?;
artifact.warns.write_all_stderr();
Ok(artifact.object.to_string()) Ok(artifact.object.to_string())
} }
} }
@ -148,10 +156,13 @@ impl HIRBuilder {
mode: &str, mode: &str,
) -> Result<CompleteArtifact, IncompleteArtifact> { ) -> Result<CompleteArtifact, IncompleteArtifact> {
let mut ast_builder = ASTBuilder::new(self.cfg().copy()); let mut ast_builder = ASTBuilder::new(self.cfg().copy());
let ast = ast_builder.build(src).map_err(|errs| { let artifact = ast_builder
IncompleteArtifact::new(None, CompileErrors::from(errs), CompileErrors::empty()) .build(src)
})?; .map_err(|iart| IncompleteArtifact::new(None, iart.errors.into(), iart.warns.into()))?;
self.check(ast, mode) self.lowerer
.warns
.extend(LowerWarnings::from(artifact.warns));
self.check(artifact.ast, mode)
} }
pub fn pop_mod_ctx(&mut self) -> Option<ModuleContext> { pub fn pop_mod_ctx(&mut self) -> Option<ModuleContext> {

View file

@ -7,9 +7,10 @@ use std::process;
use crate::ty::codeobj::{CodeObj, CodeObjFlags, MakeFunctionFlags}; use crate::ty::codeobj::{CodeObj, CodeObjFlags, MakeFunctionFlags};
use crate::ty::value::GenTypeObj; use crate::ty::value::GenTypeObj;
use erg_common::cache::CacheSet; use erg_common::cache::CacheSet;
use erg_common::config::{ErgConfig, Input}; use erg_common::config::ErgConfig;
use erg_common::env::erg_std_path; use erg_common::env::erg_std_path;
use erg_common::error::{ErrorDisplay, Location}; use erg_common::error::{ErrorDisplay, Location};
use erg_common::io::Input;
use erg_common::opcode::{CommonOpcode, CompareOp}; use erg_common::opcode::{CommonOpcode, CompareOp};
use erg_common::opcode308::Opcode308; use erg_common::opcode308::Opcode308;
use erg_common::opcode310::Opcode310; use erg_common::opcode310::Opcode310;
@ -646,8 +647,8 @@ impl PyCodeGenerator {
} }
StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind { StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind {
Name => LOAD_NAME as u8, Name => LOAD_NAME as u8,
Attr => LOAD_ATTR as u8, UnboundAttr => LOAD_ATTR as u8,
Method => LOAD_METHOD as u8, BoundAttr => LOAD_METHOD as u8,
}, },
} }
} }
@ -669,9 +670,9 @@ impl PyCodeGenerator {
StoreLoadKind::Local | StoreLoadKind::LocalConst => { StoreLoadKind::Local | StoreLoadKind::LocalConst => {
match acc_kind { match acc_kind {
Name => STORE_NAME as u8, Name => STORE_NAME as u8,
Attr => STORE_ATTR as u8, UnboundAttr => STORE_ATTR as u8,
// cannot overwrite methods directly // cannot overwrite methods directly
Method => STORE_ATTR as u8, BoundAttr => STORE_ATTR as u8,
} }
} }
} }
@ -792,9 +793,9 @@ impl PyCodeGenerator {
log!(info "entered {} ({ident})", fn_name!()); log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident); let escaped = escape_ident(ident);
let name = self let name = self
.local_search(&escaped, Attr) .local_search(&escaped, UnboundAttr)
.unwrap_or_else(|| self.register_attr(escaped)); .unwrap_or_else(|| self.register_attr(escaped));
let instr = self.select_load_instr(name.kind, Attr); let instr = self.select_load_instr(name.kind, UnboundAttr);
self.write_instr(instr); self.write_instr(instr);
self.write_arg(name.idx); self.write_arg(name.idx);
if self.py_version.minor >= Some(11) { if self.py_version.minor >= Some(11) {
@ -809,9 +810,9 @@ impl PyCodeGenerator {
} }
let escaped = escape_ident(ident); let escaped = escape_ident(ident);
let name = self let name = self
.local_search(&escaped, Method) .local_search(&escaped, BoundAttr)
.unwrap_or_else(|| self.register_method(escaped)); .unwrap_or_else(|| self.register_method(escaped));
let instr = self.select_load_instr(name.kind, Method); let instr = self.select_load_instr(name.kind, BoundAttr);
self.write_instr(instr); self.write_instr(instr);
self.write_arg(name.idx); self.write_arg(name.idx);
if self.py_version.minor >= Some(11) { if self.py_version.minor >= Some(11) {
@ -866,7 +867,7 @@ impl PyCodeGenerator {
} }
Accessor::Attr(attr) => { Accessor::Attr(attr) => {
self.emit_expr(*attr.obj); self.emit_expr(*attr.obj);
self.emit_store_instr(attr.ident, Attr); self.emit_store_instr(attr.ident, UnboundAttr);
} }
} }
} }
@ -1010,7 +1011,7 @@ impl PyCodeGenerator {
self.emit_precall_and_call(argc); self.emit_precall_and_call(argc);
} else { } else {
match kind { match kind {
AccessKind::Method => self.write_instr(Opcode310::CALL_METHOD), AccessKind::BoundAttr => self.write_instr(Opcode310::CALL_METHOD),
_ => self.write_instr(Opcode310::CALL_FUNCTION), _ => self.write_instr(Opcode310::CALL_FUNCTION),
} }
self.write_arg(argc); self.write_arg(argc);
@ -2188,7 +2189,7 @@ impl PyCodeGenerator {
let is_py_api = method_name.is_py_api(); let is_py_api = method_name.is_py_api();
self.emit_expr(obj); self.emit_expr(obj);
self.emit_load_method_instr(method_name); self.emit_load_method_instr(method_name);
self.emit_args_311(args, Method, is_py_api); self.emit_args_311(args, BoundAttr, is_py_api);
} }
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) { fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
@ -3121,7 +3122,7 @@ impl PyCodeGenerator {
self.emit_load_name_instr(Identifier::private("#path")); self.emit_load_name_instr(Identifier::private("#path"));
self.emit_load_method_instr(Identifier::public("append")); self.emit_load_method_instr(Identifier::public("append"));
self.emit_load_const(erg_std_path().to_str().unwrap()); self.emit_load_const(erg_std_path().to_str().unwrap());
self.emit_call_instr(1, Method); self.emit_call_instr(1, BoundAttr);
self.stack_dec(); self.stack_dec();
self.emit_pop_top(); self.emit_pop_top();
let erg_std_mod = Identifier::public("_erg_std_prelude"); let erg_std_mod = Identifier::public("_erg_std_prelude");

View file

@ -80,19 +80,31 @@ impl Name {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessKind { pub enum AccessKind {
Name, Name,
Attr, /// class/module attr
Method, /// e.g. `Str.center`
UnboundAttr,
/// method/instance attr
/// e.g. `"aaa".center`
///
/// can also access class/module attrs
BoundAttr,
} }
impl AccessKind { impl AccessKind {
pub const fn is_local(&self) -> bool { pub const fn is_local(&self) -> bool {
matches!(self, Self::Name) matches!(self, Self::Name)
} }
pub const fn is_attr(&self) -> bool { pub const fn is_unbound_attr(&self) -> bool {
matches!(self, Self::Attr) matches!(self, Self::UnboundAttr)
}
pub const fn is_bound_attr(&self) -> bool {
matches!(self, Self::BoundAttr)
}
pub fn matches(&self, vi: &VarInfo) -> bool {
match self {
Self::Name | Self::BoundAttr => true,
Self::UnboundAttr => !vi.kind.is_instance_attr(),
} }
pub const fn is_method(&self) -> bool {
matches!(self, Self::Method)
} }
} }
@ -155,19 +167,19 @@ impl Runnable for Compiler {
let warns = self let warns = self
.compile_and_dump_as_pyc(path, src, "exec") .compile_and_dump_as_pyc(path, src, "exec")
.map_err(|eart| { .map_err(|eart| {
eart.warns.fmt_all_stderr(); eart.warns.write_all_stderr();
eart.errors eart.errors
})?; })?;
warns.fmt_all_stderr(); warns.write_all_stderr();
Ok(ExitStatus::compile_passed(warns.len())) Ok(ExitStatus::compile_passed(warns.len()))
} }
fn eval(&mut self, src: String) -> Result<String, CompileErrors> { fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
let arti = self.compile(src, "eval").map_err(|eart| { let arti = self.compile(src, "eval").map_err(|eart| {
eart.warns.fmt_all_stderr(); eart.warns.write_all_stderr();
eart.errors eart.errors
})?; })?;
arti.warns.fmt_all_stderr(); arti.warns.write_all_stderr();
Ok(arti.object.code_info(Some(self.code_generator.py_version))) Ok(arti.object.code_info(Some(self.code_generator.py_version)))
} }
} }

View file

@ -301,6 +301,13 @@ impl Context {
panic!("err: {err}"); panic!("err: {err}");
} }
} }
} else if typ.has_undoable_linked_var() {
if let Err(err) = self.overwrite_typarams(typ, rhs) {
Self::undo_substitute_typarams(typ);
if DEBUG_MODE {
panic!("err: {err}");
}
}
} }
for rhs_sup in rhs_ctx.super_traits.iter() { for rhs_sup in rhs_ctx.super_traits.iter() {
// Not `supertype_of` (only structures are compared) // Not `supertype_of` (only structures are compared)
@ -459,8 +466,8 @@ impl Context {
// => ?P.undoable_link(Int) // => ?P.undoable_link(Int)
// => Mul Int :> Int // => Mul Int :> Int
(FreeVar(lfv), rhs) => { (FreeVar(lfv), rhs) => {
if let FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } = &*lfv.borrow() { if let Some(t) = lfv.get_linked() {
return self.supertype_of(t, rhs); return self.supertype_of(&t, rhs);
} }
if let Some((_sub, sup)) = lfv.get_subsup() { if let Some((_sub, sup)) = lfv.get_subsup() {
lfv.undoable_link(rhs); lfv.undoable_link(rhs);
@ -482,8 +489,8 @@ impl Context {
} }
} }
(lhs, FreeVar(rfv)) => { (lhs, FreeVar(rfv)) => {
if let FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } = &*rfv.borrow() { if let Some(t) = rfv.get_linked() {
return self.supertype_of(lhs, t); return self.supertype_of(lhs, &t);
} }
if let Some((sub, _sup)) = rfv.get_subsup() { if let Some((sub, _sup)) = rfv.get_subsup() {
rfv.undoable_link(lhs); rfv.undoable_link(lhs);
@ -908,16 +915,24 @@ impl Context {
} }
} }
_ => { _ => {
if let (Ok(sup), Ok(sub)) = ( match (
self.convert_tp_into_type(sup_p.clone()), self.convert_tp_into_type(sup_p.clone()),
self.convert_tp_into_type(sub_p.clone()), self.convert_tp_into_type(sub_p.clone()),
) { ) {
(Ok(sup), Ok(sub)) => {
return match variance { return match variance {
Variance::Contravariant => self.subtype_of(&sup, &sub), Variance::Contravariant => self.subtype_of(&sup, &sub),
Variance::Covariant => self.supertype_of(&sup, &sub), Variance::Covariant => self.supertype_of(&sup, &sub),
Variance::Invariant => self.same_type_of(&sup, &sub), Variance::Invariant => self.same_type_of(&sup, &sub),
}; };
} }
(Err(le), Err(re)) => {
log!(err "cannot convert {le}, {re} to types")
}
(Err(err), _) | (_, Err(err)) => {
log!(err "cannot convert {err} to a type");
}
}
self.eq_tp(sup_p, sub_p) self.eq_tp(sup_p, sub_p)
} }
} }

View file

@ -1338,6 +1338,11 @@ impl Context {
TyParam::FreeVar(fv) if fv.is_linked() => self.convert_tp_into_type(fv.crack().clone()), TyParam::FreeVar(fv) if fv.is_linked() => self.convert_tp_into_type(fv.crack().clone()),
TyParam::Type(t) => Ok(t.as_ref().clone()), TyParam::Type(t) => Ok(t.as_ref().clone()),
TyParam::Mono(name) => Ok(Type::Mono(name)), TyParam::Mono(name) => Ok(Type::Mono(name)),
TyParam::App { name, args } => Ok(Type::Poly { name, params: args }),
TyParam::Proj { obj, attr } => {
let lhs = self.convert_tp_into_type(*obj)?;
Ok(lhs.proj(attr))
}
// TyParam::Erased(_t) => Ok(Type::Obj), // TyParam::Erased(_t) => Ok(Type::Obj),
TyParam::Value(v) => self.convert_value_into_type(v).map_err(TyParam::Value), TyParam::Value(v) => self.convert_value_into_type(v).map_err(TyParam::Value),
// TODO: Dict, Set // TODO: Dict, Set
@ -1650,6 +1655,20 @@ impl Context {
Ok(()) Ok(())
} }
pub(crate) fn overwrite_typarams(&self, qt: &Type, st: &Type) -> EvalResult<()> {
let qtps = qt.typarams();
let stps = st.typarams();
if qt.qual_name() != st.qual_name() || qtps.len() != stps.len() {
log!(err "{qt} / {st}");
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
}
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
self.overwrite_typaram(qtp, stp)?;
}
Ok(())
}
fn substitute_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> { fn substitute_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp { match qtp {
TyParam::FreeVar(ref fv) if fv.is_generalized() => { TyParam::FreeVar(ref fv) if fv.is_generalized() => {
@ -1693,6 +1712,49 @@ impl Context {
Ok(()) Ok(())
} }
fn overwrite_typaram(&self, qtp: TyParam, stp: TyParam) -> EvalResult<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_undoable_linked() => {
if !stp.is_unbound_var() || !stp.is_generalized() {
fv.undoable_link(&stp);
}
if let Err(errs) = self.sub_unify_tp(&stp, &qtp, None, &(), false) {
log!(err "{errs}");
}
Ok(())
}
TyParam::Type(qt) => self.overwrite_type(stp, *qt),
TyParam::Value(ValueObj::Type(qt)) => self.overwrite_type(stp, qt.into_typ()),
_ => Ok(()),
}
}
fn overwrite_type(&self, stp: TyParam, qt: Type) -> EvalResult<()> {
let st = self.convert_tp_into_type(stp).map_err(|tp| {
EvalError::not_a_type_error(
self.cfg.input.clone(),
line!() as usize,
().loc(),
self.caused_by(),
&tp.to_string(),
)
})?;
if qt.has_undoable_linked_var() {
if let Ok(qt) = <&FreeTyVar>::try_from(&qt) {
if !st.is_unbound_var() || !st.is_generalized() {
qt.undoable_link(&st);
}
}
}
if !st.is_unbound_var() || !st.is_generalized() {
self.overwrite_typarams(&qt, &st)?;
}
if let Err(errs) = self.sub_unify(&st, &qt, &(), None) {
log!(err "{errs}");
}
Ok(())
}
pub(crate) fn undo_substitute_typarams(substituted_q: &Type) { pub(crate) fn undo_substitute_typarams(substituted_q: &Type) {
for tp in substituted_q.typarams().into_iter() { for tp in substituted_q.typarams().into_iter() {
match tp { match tp {

View file

@ -303,7 +303,9 @@ pub(crate) fn sub_tpdict_get<'d>(
let mut matches = vec![]; let mut matches = vec![];
for (k, v) in dict.iter() { for (k, v) in dict.iter() {
match (<&Type>::try_from(key), <&Type>::try_from(k)) { match (<&Type>::try_from(key), <&Type>::try_from(k)) {
(Ok(idx), Ok(kt)) if ctx.subtype_of(&idx.lower_bounded(), &kt.lower_bounded()) => { (Ok(idx), Ok(kt))
if ctx.subtype_of(&idx.lower_bounded(), &kt.lower_bounded()) || dict.len() == 1 =>
{
matches.push((idx, kt, v)); matches.push((idx, kt, v));
} }
(_, _) if key == k => { (_, _) if key == k => {

View file

@ -2,9 +2,9 @@
use std::option::Option; // conflicting to Type::Option use std::option::Option; // conflicting to Type::Option
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use erg_common::config::Input;
use erg_common::consts::{ERG_MODE, PYTHON_MODE}; use erg_common::consts::{ERG_MODE, PYTHON_MODE};
use erg_common::error::{ErrorCore, Location, SubMessage}; use erg_common::error::{ErrorCore, Location, SubMessage};
use erg_common::io::Input;
use erg_common::levenshtein; use erg_common::levenshtein;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::traits::{Locational, NoTypeDisplay, Stream}; use erg_common::traits::{Locational, NoTypeDisplay, Stream};
@ -420,7 +420,7 @@ impl Context {
) -> Triple<VarInfo, TyCheckError> { ) -> Triple<VarInfo, TyCheckError> {
if let Some(vi) = self.get_current_scope_var(&ident.name) { if let Some(vi) = self.get_current_scope_var(&ident.name) {
match self.validate_visibility(ident, vi, input, namespace) { match self.validate_visibility(ident, vi, input, namespace) {
Ok(()) => { Ok(()) if acc_kind.matches(vi) => {
return Triple::Ok(vi.clone()); return Triple::Ok(vi.clone());
} }
Err(err) => { Err(err) => {
@ -428,6 +428,7 @@ impl Context {
return Triple::Err(err); return Triple::Err(err);
} }
} }
_ => {}
} }
} else if let Some((name, _vi)) = self } else if let Some((name, _vi)) = self
.future_defined_locals .future_defined_locals
@ -453,6 +454,17 @@ impl Context {
self.get_similar_name(ident.inspect()), self.get_similar_name(ident.inspect()),
)); ));
} }
for (_, method_ctx) in self.methods_list.iter() {
match method_ctx.rec_get_var_info(ident, acc_kind, input, namespace) {
Triple::Ok(vi) => {
return Triple::Ok(vi);
}
Triple::Err(e) => {
return Triple::Err(e);
}
Triple::None => {}
}
}
if acc_kind.is_local() { if acc_kind.is_local() {
if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) { if let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
return parent.rec_get_var_info(ident, acc_kind, input, namespace); return parent.rec_get_var_info(ident, acc_kind, input, namespace);
@ -468,7 +480,7 @@ impl Context {
) -> Option<&mut VarInfo> { ) -> Option<&mut VarInfo> {
if let Some(vi) = self.get_current_scope_var(&ident.name) { if let Some(vi) = self.get_current_scope_var(&ident.name) {
match self.validate_visibility(ident, vi, &self.cfg.input, self) { match self.validate_visibility(ident, vi, &self.cfg.input, self) {
Ok(()) => { Ok(()) if acc_kind.matches(vi) => {
let vi = self.get_mut_current_scope_var(&ident.name).unwrap(); let vi = self.get_mut_current_scope_var(&ident.name).unwrap();
return Some(vi); return Some(vi);
} }
@ -477,6 +489,7 @@ impl Context {
return None; return None;
} }
} }
_ => {}
} }
} }
if acc_kind.is_local() { if acc_kind.is_local() {
@ -500,7 +513,7 @@ impl Context {
.or_else(|| self.future_defined_locals.get(&ident.inspect()[..])) .or_else(|| self.future_defined_locals.get(&ident.inspect()[..]))
{ {
match self.validate_visibility(ident, vi, input, namespace) { match self.validate_visibility(ident, vi, input, namespace) {
Ok(()) => { Ok(()) if acc_kind.matches(vi) => {
return Triple::Ok(vi.clone()); return Triple::Ok(vi.clone());
} }
Err(err) => { Err(err) => {
@ -508,6 +521,7 @@ impl Context {
return Triple::Err(err); return Triple::Err(err);
} }
} }
_ => {}
} }
} }
if acc_kind.is_local() { if acc_kind.is_local() {
@ -562,9 +576,10 @@ impl Context {
} }
_ => {} _ => {}
} }
// class/module attr
if let Ok(singular_ctxs) = self.get_singular_ctxs_by_hir_expr(obj, namespace) { if let Ok(singular_ctxs) = self.get_singular_ctxs_by_hir_expr(obj, namespace) {
for ctx in singular_ctxs { for ctx in singular_ctxs {
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) { match ctx.rec_get_var_info(ident, AccessKind::UnboundAttr, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
return Triple::Ok(vi); return Triple::Ok(vi);
} }
@ -575,7 +590,8 @@ impl Context {
} }
} }
} }
match self.get_attr_from_nominal_t(obj, ident, input, namespace) { // bound method/instance attr
match self.get_bound_attr_from_nominal_t(obj, ident, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
if let Some(self_t) = vi.t.self_t() { if let Some(self_t) = vi.t.self_t() {
match self match self
@ -636,7 +652,7 @@ impl Context {
Triple::None Triple::None
} }
fn get_attr_from_nominal_t( fn get_bound_attr_from_nominal_t(
&self, &self,
obj: &hir::Expr, obj: &hir::Expr,
ident: &Identifier, ident: &Identifier,
@ -646,7 +662,7 @@ impl Context {
let self_t = obj.t(); let self_t = obj.t();
if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) { if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) {
for ctx in sups { for ctx in sups {
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) { match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
return Triple::Ok(vi); return Triple::Ok(vi);
} }
@ -657,7 +673,7 @@ impl Context {
} }
// if self is a methods context // if self is a methods context
if let Some(ctx) = self.get_same_name_context(&ctx.name) { if let Some(ctx) = self.get_same_name_context(&ctx.name) {
match ctx.rec_get_var_info(ident, AccessKind::Method, input, namespace) { match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
return Triple::Ok(vi); return Triple::Ok(vi);
} }
@ -691,7 +707,7 @@ impl Context {
} }
}; };
for ctx in ctxs { for ctx in ctxs {
match ctx.rec_get_var_info(ident, AccessKind::Attr, input, namespace) { match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
obj.ref_t().coerce(); obj.ref_t().coerce();
return Triple::Ok(vi); return Triple::Ok(vi);
@ -702,7 +718,7 @@ impl Context {
_ => {} _ => {}
} }
if let Some(ctx) = self.get_same_name_context(&ctx.name) { if let Some(ctx) = self.get_same_name_context(&ctx.name) {
match ctx.rec_get_var_info(ident, AccessKind::Method, input, namespace) { match ctx.rec_get_var_info(ident, AccessKind::BoundAttr, input, namespace) {
Triple::Ok(vi) => { Triple::Ok(vi) => {
return Triple::Ok(vi); return Triple::Ok(vi);
} }
@ -946,7 +962,7 @@ impl Context {
} }
} }
if let Some(ctx) = self.get_same_name_context(&ctx.name) { if let Some(ctx) = self.get_same_name_context(&ctx.name) {
match ctx.rec_get_var_info(attr_name, AccessKind::Method, input, namespace) { match ctx.rec_get_var_info(attr_name, AccessKind::BoundAttr, input, namespace) {
Triple::Ok(t) => { Triple::Ok(t) => {
return Ok(t); return Ok(t);
} }
@ -3072,4 +3088,58 @@ impl Context {
))) )))
} }
} }
pub(crate) fn get_instance_attr(&self, name: &str) -> Option<&VarInfo> {
if let Some(vi) = self.locals.get(name) {
if vi.kind.is_instance_attr() {
return Some(vi);
}
}
if let Some(vi) = self.decls.get(name) {
if vi.kind.is_instance_attr() {
return Some(vi);
}
}
if self.kind.is_method_def() {
self.get_nominal_type_ctx(&mono(&self.name))
.and_then(|(_, ctx)| ctx.get_instance_attr(name))
} else {
self.methods_list.iter().find_map(|(_, ctx)| {
if ctx.kind.is_trait_impl() {
None
} else {
ctx.get_instance_attr(name)
}
})
}
}
/// does not remove instance attribute declarations
pub(crate) fn remove_class_attr(&mut self, name: &str) -> Option<(VarName, VarInfo)> {
if let Some((k, v)) = self.locals.remove_entry(name) {
if v.kind.is_instance_attr() {
self.locals.insert(k, v);
} else {
return Some((k, v));
}
} else if let Some((k, v)) = self.decls.remove_entry(name) {
if v.kind.is_instance_attr() {
self.decls.insert(k, v);
} else {
return Some((k, v));
}
}
if self.kind.is_method_def() {
self.get_mut_nominal_type_ctx(&mono(&self.name))
.and_then(|(_, ctx)| ctx.remove_class_attr(name))
} else {
self.methods_list.iter_mut().find_map(|(_, ctx)| {
if ctx.kind.is_trait_impl() {
None
} else {
ctx.remove_class_attr(name)
}
})
}
}
} }

View file

@ -574,19 +574,23 @@ impl Context {
) -> TyCheckResult<Type> { ) -> TyCheckResult<Type> {
match poly_spec.acc.to_string().trim_start_matches([':', '.']) { match poly_spec.acc.to_string().trim_start_matches([':', '.']) {
"Array" => { "Array" => {
let ctx = self
.get_nominal_type_ctx(&array_t(Type::Obj, TyParam::Failure))
.unwrap()
.1;
// TODO: kw // TODO: kw
let mut args = poly_spec.args.pos_args(); let mut args = poly_spec.args.pos_args();
if let Some(first) = args.next() { if let Some(first) = args.next() {
let t = self.instantiate_const_expr_as_type( let t = self.instantiate_const_expr_as_type(
&first.expr, &first.expr,
None, Some((ctx, 0)),
tmp_tv_cache, tmp_tv_cache,
not_found_is_qvar, not_found_is_qvar,
)?; )?;
let len = if let Some(len) = args.next() { let len = if let Some(len) = args.next() {
self.instantiate_const_expr( self.instantiate_const_expr(
&len.expr, &len.expr,
None, Some((ctx, 1)),
tmp_tv_cache, tmp_tv_cache,
not_found_is_qvar, not_found_is_qvar,
)? )?
@ -824,21 +828,24 @@ impl Context {
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar) self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
} }
ast::ConstExpr::App(app) => { ast::ConstExpr::App(app) => {
let name = match &app.acc { let ast::ConstAccessor::Local(ident) = &app.acc else {
ast::ConstAccessor::Local(local) => local.inspect(), return type_feature_error!(self, app.loc(), "instantiating const callee");
_ => return type_feature_error!(self, app.loc(), "instantiating const callee"),
}; };
let &ctx = self
.get_singular_ctxs_by_ident(ident, self)?
.first()
.unwrap_or(&self);
let mut args = vec![]; let mut args = vec![];
for (i, arg) in app.args.pos_args().enumerate() { for (i, arg) in app.args.pos_args().enumerate() {
let arg_t = self.instantiate_const_expr( let arg_t = self.instantiate_const_expr(
&arg.expr, &arg.expr,
Some((self, i)), Some((ctx, i)),
tmp_tv_cache, tmp_tv_cache,
not_found_is_qvar, not_found_is_qvar,
)?; )?;
args.push(arg_t); args.push(arg_t);
} }
Ok(TyParam::app(name.clone(), args)) Ok(TyParam::app(ident.inspect().clone(), args))
} }
ast::ConstExpr::Array(ConstArray::Normal(array)) => { ast::ConstExpr::Array(ConstArray::Normal(array)) => {
let mut tp_arr = vec![]; let mut tp_arr = vec![];

View file

@ -319,6 +319,10 @@ impl ContextKind {
matches!(self, Self::MethodDefs(_)) matches!(self, Self::MethodDefs(_))
} }
pub const fn is_trait_impl(&self) -> bool {
matches!(self, Self::MethodDefs(Some(_)))
}
pub const fn is_type(&self) -> bool { pub const fn is_type(&self) -> bool {
matches!(self, Self::Class | Self::Trait | Self::StructuralTrait) matches!(self, Self::Class | Self::Trait | Self::StructuralTrait)
} }

View file

@ -9,6 +9,7 @@ use std::time::{Duration, SystemTime};
use erg_common::config::ErgMode; use erg_common::config::ErgMode;
use erg_common::consts::{ERG_MODE, PYTHON_MODE}; use erg_common::consts::{ERG_MODE, PYTHON_MODE};
use erg_common::dict::Dict;
use erg_common::env::{is_pystd_main_module, is_std_decl_path}; use erg_common::env::{is_pystd_main_module, is_std_decl_path};
use erg_common::erg_util::BUILTIN_ERG_MODS; use erg_common::erg_util::BUILTIN_ERG_MODS;
use erg_common::levenshtein::get_similar_name; use erg_common::levenshtein::get_similar_name;
@ -32,7 +33,7 @@ use crate::ty::free::{Constraint, HasLevel};
use crate::ty::typaram::TyParam; use crate::ty::typaram::TyParam;
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj}; use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{ use crate::ty::{
GuardType, HasType, ParamTy, SubrType, Type, Variable, Visibility, VisibilityModifier, Field, GuardType, HasType, ParamTy, SubrType, Type, Variable, Visibility, VisibilityModifier,
}; };
use crate::build_hir::HIRBuilder; use crate::build_hir::HIRBuilder;
@ -200,7 +201,10 @@ impl Context {
} else { } else {
None None
}; };
if let Some(_decl) = self.decls.remove(&ident.name) { if self
.remove_class_attr(ident.name.inspect())
.is_some_and(|(_, decl)| !decl.kind.is_auto())
{
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error( Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
@ -266,7 +270,10 @@ impl Context {
self.absolutize(sig.ident.name.loc()), self.absolutize(sig.ident.name.loc()),
); );
self.index().register(&vi); self.index().register(&vi);
if let Some(_decl) = self.decls.remove(name) { if self
.remove_class_attr(name)
.is_some_and(|(_, decl)| !decl.kind.is_auto())
{
Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error( Err(TyCheckErrors::from(TyCheckError::duplicate_decl_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
@ -452,6 +459,7 @@ impl Context {
if self if self
.registered_info(name.inspect(), name.is_const()) .registered_info(name.inspect(), name.is_const())
.is_some() .is_some()
&& &name.inspect()[..] != "_"
{ {
Err(TyCheckErrors::from(TyCheckError::reassign_error( Err(TyCheckErrors::from(TyCheckError::reassign_error(
self.cfg.input.clone(), self.cfg.input.clone(),
@ -1287,9 +1295,7 @@ impl Context {
2, 2,
self.level, self.level,
); );
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?; self.gen_class_new_method(&gen, &mut ctx)?;
}
self.register_gen_mono_type(ident, gen, ctx, Const) self.register_gen_mono_type(ident, gen, ctx, Const)
} else { } else {
let params = gen let params = gen
@ -1306,9 +1312,7 @@ impl Context {
2, 2,
self.level, self.level,
); );
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?; self.gen_class_new_method(&gen, &mut ctx)?;
}
self.register_gen_poly_type(ident, gen, ctx, Const) self.register_gen_poly_type(ident, gen, ctx, Const)
} }
} }
@ -1350,16 +1354,7 @@ impl Context {
.. ..
} = additional } = additional
{ {
for (field, t) in rec.iter() { self.register_instance_attrs(&mut ctx, rec)?;
let varname = VarName::from_str(field.symbol.clone());
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls.insert(varname, vi);
}
} }
param_t param_t
.map(|t| self.intersection(t, additional.typ())) .map(|t| self.intersection(t, additional.typ()))
@ -1421,16 +1416,7 @@ impl Context {
self.level, self.level,
); );
let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") }; let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") };
for (field, t) in req.iter() { self.register_instance_attrs(&mut ctx, req)?;
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls
.insert(VarName::from_str(field.symbol.clone()), vi);
}
self.register_gen_mono_type(ident, gen, ctx, Const) self.register_gen_mono_type(ident, gen, ctx, Const)
} else { } else {
feature_error!( feature_error!(
@ -1463,16 +1449,7 @@ impl Context {
None None
}; };
if let Some(additional) = additional { if let Some(additional) = additional {
for (field, t) in additional.iter() { self.register_instance_attrs(&mut ctx, additional)?;
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls
.insert(VarName::from_str(field.symbol.clone()), vi);
}
} }
for sup in super_classes.into_iter() { for sup in super_classes.into_iter() {
if let Some((_, sup_ctx)) = self.get_nominal_type_ctx(&sup) { if let Some((_, sup_ctx)) = self.get_nominal_type_ctx(&sup) {
@ -1524,6 +1501,29 @@ impl Context {
} }
} }
fn register_instance_attrs(
&self,
ctx: &mut Context,
rec: &Dict<Field, Type>,
) -> CompileResult<()> {
for (field, t) in rec.iter() {
let varname = VarName::from_str(field.symbol.clone());
let vi =
VarInfo::instance_attr(field.clone(), t.clone(), self.impl_of(), ctx.name.clone());
// self.index().register(&vi);
if let Some(_ent) = ctx.decls.insert(varname.clone(), vi) {
return Err(CompileErrors::from(CompileError::duplicate_decl_error(
self.cfg.input.clone(),
line!() as usize,
varname.loc(),
self.caused_by(),
varname.inspect(),
)));
}
}
Ok(())
}
fn gen_class_new_method(&self, gen: &GenTypeObj, ctx: &mut Context) -> CompileResult<()> { fn gen_class_new_method(&self, gen: &GenTypeObj, ctx: &mut Context) -> CompileResult<()> {
let mut methods = Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level); let mut methods = Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
let new_t = if let Some(base) = gen.base_or_sup() { let new_t = if let Some(base) = gen.base_or_sup() {
@ -1532,16 +1532,7 @@ impl Context {
t: Type::Record(rec), t: Type::Record(rec),
.. ..
} => { } => {
for (field, t) in rec.iter() { self.register_instance_attrs(ctx, rec)?;
let varname = VarName::from_str(field.symbol.clone());
let vi = VarInfo::instance_attr(
field.clone(),
t.clone(),
self.impl_of(),
ctx.name.clone(),
);
ctx.decls.insert(varname, vi);
}
} }
other => { other => {
methods.register_fixed_auto_impl( methods.register_fixed_auto_impl(
@ -1557,6 +1548,7 @@ impl Context {
} else { } else {
func0(gen.typ().clone()) func0(gen.typ().clone())
}; };
if ERG_MODE {
methods.register_fixed_auto_impl( methods.register_fixed_auto_impl(
"__new__", "__new__",
new_t.clone(), new_t.clone(),
@ -1564,9 +1556,23 @@ impl Context {
Visibility::BUILTIN_PRIVATE, Visibility::BUILTIN_PRIVATE,
Some("__call__".into()), Some("__call__".into()),
)?; )?;
// 必要なら、ユーザーが独自に上書きする
// users can override this if necessary // users can override this if necessary
methods.register_auto_impl("new", new_t, Immutable, Visibility::BUILTIN_PUBLIC, None)?; methods.register_auto_impl(
"new",
new_t,
Immutable,
Visibility::BUILTIN_PUBLIC,
None,
)?;
} else {
methods.register_auto_impl(
"__call__",
new_t,
Immutable,
Visibility::BUILTIN_PUBLIC,
Some("__call__".into()),
)?;
}
ctx.methods_list ctx.methods_list
.push((ClassDefType::Simple(gen.typ().clone()), methods)); .push((ClassDefType::Simple(gen.typ().clone()), methods));
Ok(()) Ok(())

View file

@ -446,11 +446,13 @@ impl Context {
Ok(()) Ok(())
} }
(TyParam::Dict(sub), TyParam::Dict(sup)) => { (TyParam::Dict(sub), TyParam::Dict(sup)) => {
for (lk, lv) in sub.iter() { for (sub_k, sub_v) in sub.iter() {
if let Some(rv) = sup.get(lk).or_else(|| sub_tpdict_get(sup, lk, self)) { if let Some(sup_v) = sup.get(sub_k).or_else(|| sub_tpdict_get(sup, sub_k, self))
self.sub_unify_tp(lv, rv, _variance, loc, allow_divergence)?; {
// self.sub_unify_tp(sub_k, sup_k, _variance, loc, allow_divergence)?;
self.sub_unify_tp(sub_v, sup_v, _variance, loc, allow_divergence)?;
} else { } else {
log!(err "{sup} does not have key {lk}"); log!(err "{sup} does not have key {sub_k}");
// TODO: // TODO:
return Err(TyCheckErrors::from(TyCheckError::unreachable( return Err(TyCheckErrors::from(TyCheckError::unreachable(
self.cfg.input.clone(), self.cfg.input.clone(),

View file

@ -177,7 +177,7 @@ impl ASTLowerer {
for ctx in ctxs { for ctx in ctxs {
if let Triple::Ok(vi) = ctx.rec_get_var_info( if let Triple::Ok(vi) = ctx.rec_get_var_info(
&ident.raw, &ident.raw,
AccessKind::Attr, AccessKind::UnboundAttr,
self.input(), self.input(),
&self.module.context, &self.module.context,
) { ) {

View file

@ -1,5 +1,5 @@
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage}; use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage};
use erg_common::io::Input;
use erg_common::switch_lang; use erg_common::switch_lang;
use crate::error::*; use crate::error::*;

View file

@ -1,5 +1,5 @@
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage}; use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage};
use erg_common::io::Input;
use erg_common::style::{StyledStr, StyledString, StyledStrings, Stylize}; use erg_common::style::{StyledStr, StyledString, StyledStrings, Stylize};
use erg_common::traits::Locational; use erg_common::traits::Locational;
use erg_common::{switch_lang, Str}; use erg_common::{switch_lang, Str};
@ -485,11 +485,12 @@ impl LowerError {
) -> Self { ) -> Self {
let hint = similar_name.map(|n| { let hint = similar_name.map(|n| {
let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display()); let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display());
let kind = similar_info.map_or("", |vi| vi.kind.display());
switch_lang!( switch_lang!(
"japanese" => format!("似た名前の{vis}属性があります: {n}"), "japanese" => format!("似た名前の{vis}{kind}属性があります: {n}"),
"simplified_chinese" => format!("具有相同名称的{vis}属性: {n}"), "simplified_chinese" => format!("具有相同名称的{vis}{kind}属性: {n}"),
"traditional_chinese" => format!("具有相同名稱的{vis}屬性: {n}"), "traditional_chinese" => format!("具有相同名稱的{vis}{kind}屬性: {n}"),
"english" => format!("has a similar name {vis} attribute: {n}"), "english" => format!("has a similar name {vis} {kind} attribute: {n}"),
) )
}); });
let found = StyledString::new(name, Some(ERR), Some(ATTR)); let found = StyledString::new(name, Some(ERR), Some(ATTR));
@ -1157,4 +1158,30 @@ impl LowerWarning {
caused_by, caused_by,
) )
} }
pub fn same_name_instance_attr_warning(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = StyledStr::new(readable_name(name), Some(WARN), Some(ATTR));
Self::new(
ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("同名のインスタンス属性{name}が存在します"),
"simplified_chinese" => format!("同名的实例属性{name}已存在"),
"traditional_chinese" => format!("同名的實例屬性{name}已存在"),
"english" => format!("an instance attribute named {name} already exists"),
),
errno,
NameWarning,
loc,
),
input,
caused_by,
)
}
} }

View file

@ -4,15 +4,15 @@ pub mod tycheck;
use std::fmt; use std::fmt;
use erg_common::config::Input;
use erg_common::error::{ use erg_common::error::{
ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage, ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage,
}; };
use erg_common::io::Input;
use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, Theme, THEME}; use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, Theme, THEME};
use erg_common::traits::{Locational, Stream}; use erg_common::traits::{Locational, Stream};
use erg_common::{impl_display_and_error, impl_stream, switch_lang}; use erg_common::{impl_display_and_error, impl_stream, switch_lang};
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors}; use erg_parser::error::{ParseError, ParseErrors, ParserRunnerError, ParserRunnerErrors};
pub use crate::error::eval::*; pub use crate::error::eval::*;
pub use crate::error::lower::*; pub use crate::error::lower::*;
@ -497,6 +497,18 @@ impl From<CompileError> for CompileErrors {
} }
} }
impl From<CompileError> for ParseError {
fn from(err: CompileError) -> Self {
Self::new(*err.core)
}
}
impl From<CompileErrors> for ParseErrors {
fn from(err: CompileErrors) -> Self {
Self::new(err.into_iter().map(ParseError::from).collect())
}
}
impl MultiErrorDisplay<CompileError> for CompileErrors {} impl MultiErrorDisplay<CompileError> for CompileErrors {}
impl fmt::Display for CompileErrors { impl fmt::Display for CompileErrors {
@ -524,7 +536,7 @@ mod test {
ty::{Predicate, Type}, ty::{Predicate, Type},
varinfo::{AbsLocation, VarInfo}, varinfo::{AbsLocation, VarInfo},
}; };
use erg_common::{config::Input, error::Location}; use erg_common::{error::Location, io::Input};
use erg_parser::ast::{VarName, VisModifierSpec}; use erg_parser::ast::{VarName, VisModifierSpec};
// These Erg codes are not correct grammar. // These Erg codes are not correct grammar.

View file

@ -1,7 +1,7 @@
use std::fmt::Display; use std::fmt::Display;
use erg_common::config::Input;
use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage}; use erg_common::error::{ErrorCore, ErrorKind::*, Location, SubMessage};
use erg_common::io::Input;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::style::{StyledStr, StyledString, StyledStrings, Stylize}; use erg_common::style::{StyledStr, StyledString, StyledStrings, Stylize};
use erg_common::traits::{Locational, NoTypeDisplay}; use erg_common::traits::{Locational, NoTypeDisplay};

View file

@ -8,6 +8,9 @@
dtype: Type dtype: Type
size: Nat size: Nat
.nan: Float
.Nan: Float
.abs: |T|(object: .NDArray(T),) -> .NDArray(T) .abs: |T|(object: .NDArray(T),) -> .NDArray(T)
.add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T) .add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T)
.all: |T <: Num|(object: .NDArray(T),) -> Bool .all: |T <: Num|(object: .NDArray(T),) -> Bool

View file

View file

@ -0,0 +1,5 @@
{
.DataFrame!;
.Series!;
.Index;
} = pyimport "core/api"

View file

@ -0,0 +1,3 @@
{.DataFrame!;} = pyimport "./frame"
{.Series!;} = pyimport "./series"
{.Index;} = pyimport "./indexes/api"

View file

@ -0,0 +1,14 @@
{.Index;} = pyimport "./indexes/api"
# I := Nat, V := Obj
.DataFrame!: (C: Type, I: Type, V: Type) -> ClassType
.DataFrame!(C, I, V) <: Input(C)
.DataFrame!(C, I, V) <: Input(I)
.DataFrame!(C, I, V) <: Output(V)
.DataFrame!.
__call__: |K, V, I|(dic: {K: [V; _]} or Iterable(Iterable(V)), index: [I; _] := [Nat; _]) -> .DataFrame!(K, I, V)
shape: (Nat, Nat)
index: .Index(_) # TODO
head: |C, I, V|(self: .DataFrame!(C, I, V), tail: Nat := {5}) -> .DataFrame!(C, I, V)
tail: |C, I, V|(self: .DataFrame!(C, I, V), tail: Nat := {5}) -> .DataFrame!(C, I, V)
info!: (self: .DataFrame!(_, _, _)) => NoneType

View file

@ -0,0 +1 @@
{.Index;} = pyimport "./base"

View file

@ -0,0 +1,2 @@
.Index: (T: Type) -> ClassType
.Index(T) <: Output(T)

View file

@ -0,0 +1,8 @@
{.Index;} = pyimport "./indexes/api"
# K := Nat, V := Obj
.Series!: (K: Type, V: Type) -> ClassType
.Series!(K, V) <: Input(K)
.Series!(K, V) <: Output(V)
.Series!.
__call__: |K, V|(iterable: Iterable(V), index: [K; _] or .Index(K) := [Nat; _]) -> .Series! K, V

View file

View file

View file

View file

View file

@ -215,12 +215,12 @@ impl ASTLowerer {
format!("{}{code}", "\n".repeat(first_line as usize)) format!("{}{code}", "\n".repeat(first_line as usize))
}; };
match ASTBuilder::new(self.cfg().clone()).build(code) { match ASTBuilder::new(self.cfg().clone()).build(code) {
Ok(ast) => { Ok(artifact) => {
self.check_doc_ast(ast); self.check_doc_ast(artifact.ast);
} }
Err(errs) => { Err(iart) => {
let errs = CompileErrors::from(errs); self.errs.extend(CompileErrors::from(iart.errors));
self.errs.extend(errs); self.warns.extend(CompileErrors::from(iart.warns));
} }
} }
} }

View file

@ -37,8 +37,8 @@ use crate::context::{
RegistrationMode, TraitImpl, RegistrationMode, TraitImpl,
}; };
use crate::error::{ use crate::error::{
CompileError, CompileErrors, LowerError, LowerErrors, LowerResult, LowerWarning, LowerWarnings, CompileError, CompileErrors, CompileWarning, LowerError, LowerErrors, LowerResult,
SingleLowerResult, LowerWarning, LowerWarnings, SingleLowerResult,
}; };
use crate::hir; use crate::hir;
use crate::hir::HIR; use crate::hir::HIR;
@ -125,22 +125,27 @@ impl Runnable for ASTLowerer {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> { fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy()); let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build(self.cfg.input.read())?; let artifact = ast_builder
let artifact = self .build(self.cfg.input.read())
.lower(ast, "exec")
.map_err(|artifact| artifact.errors)?; .map_err(|artifact| artifact.errors)?;
artifact.warns.fmt_all_stderr(); artifact.warns.write_all_to(&mut self.cfg.output);
println!("{}", artifact.object); let artifact = self
.lower(artifact.ast, "exec")
.map_err(|artifact| artifact.errors)?;
artifact.warns.write_all_to(&mut self.cfg.output);
use std::io::Write;
write!(self.cfg.output, "{}", artifact.object).unwrap();
Ok(ExitStatus::compile_passed(artifact.warns.len())) Ok(ExitStatus::compile_passed(artifact.warns.len()))
} }
fn eval(&mut self, src: String) -> Result<String, Self::Errs> { fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy()); let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build(src)?; let artifact = ast_builder.build(src).map_err(|artifact| artifact.errors)?;
artifact.warns.write_all_stderr();
let artifact = self let artifact = self
.lower(ast, "eval") .lower(artifact.ast, "eval")
.map_err(|artifact| artifact.errors)?; .map_err(|artifact| artifact.errors)?;
artifact.warns.fmt_all_stderr(); artifact.warns.write_all_stderr();
Ok(format!("{}", artifact.object)) Ok(format!("{}", artifact.object))
} }
} }
@ -1663,6 +1668,23 @@ impl ASTLowerer {
self.pop_append_errs(); self.pop_append_errs();
errs errs
})?; })?;
if let Some(ident) = def.sig.ident() {
if self
.module
.context
.get_instance_attr(ident.inspect())
.is_some()
{
self.warns
.push(CompileWarning::same_name_instance_attr_warning(
self.cfg.input.clone(),
line!() as usize,
ident.loc(),
self.module.context.caused_by(),
ident.inspect(),
));
}
}
} }
ast::ClassAttr::Decl(_) | ast::ClassAttr::Doc(_) => {} ast::ClassAttr::Decl(_) | ast::ClassAttr::Doc(_) => {}
} }
@ -1730,21 +1752,19 @@ impl ASTLowerer {
if let Some(sup_type) = call.args.get_left_or_key("Super") { if let Some(sup_type) = call.args.get_left_or_key("Super") {
Self::check_inheritable(&self.cfg, &mut self.errs, type_obj, sup_type, &hir_def.sig); Self::check_inheritable(&self.cfg, &mut self.errs, type_obj, sup_type, &hir_def.sig);
} }
let (__new__, need_to_gen_new) = if let (Some(dunder_new_vi), Some(new_vi)) = ( let Some(__new__) = class_ctx.get_current_scope_var(&VarName::from_static("__new__")).or(class_ctx.get_current_scope_var(&VarName::from_static("__call__"))) else {
class_ctx.get_current_scope_var(&VarName::from_static("__new__")),
class_ctx.get_current_scope_var(&VarName::from_static("new")),
) {
(dunder_new_vi.t.clone(), new_vi.kind == VarKind::Auto)
} else {
return unreachable_error!(LowerErrors, LowerError, self); return unreachable_error!(LowerErrors, LowerError, self);
}; };
let need_to_gen_new = class_ctx
.get_current_scope_var(&VarName::from_static("new"))
.map_or(false, |vi| vi.kind == VarKind::Auto);
let require_or_sup = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0)); let require_or_sup = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0));
Ok(hir::ClassDef::new( Ok(hir::ClassDef::new(
type_obj.clone(), type_obj.clone(),
hir_def.sig, hir_def.sig,
require_or_sup, require_or_sup,
need_to_gen_new, need_to_gen_new,
__new__, __new__.t.clone(),
hir_methods, hir_methods,
)) ))
} }

View file

@ -1,5 +1,6 @@
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
use erg_common::error::MultiErrorDisplay; use erg_common::error::MultiErrorDisplay;
use erg_common::io::Output;
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable; use erg_common::traits::Runnable;
@ -13,7 +14,8 @@ use erg_compiler::ty::constructors::{
use erg_compiler::ty::Type::*; use erg_compiler::ty::Type::*;
fn load_file(path: &'static str) -> Result<ModuleContext, CompileErrors> { fn load_file(path: &'static str) -> Result<ModuleContext, CompileErrors> {
let cfg = ErgConfig::with_main_path(path.into()); let mut cfg = ErgConfig::with_main_path(path.into());
cfg.output = Output::Null;
let mut lowerer = ASTLowerer::new(cfg); let mut lowerer = ASTLowerer::new(cfg);
lowerer.exec()?; lowerer.exec()?;
Ok(lowerer.pop_mod_ctx().unwrap()) Ok(lowerer.pop_mod_ctx().unwrap())
@ -26,7 +28,7 @@ fn test_infer_types() -> Result<(), ()> {
fn _test_infer_types() -> Result<(), ()> { fn _test_infer_types() -> Result<(), ()> {
let module = load_file("tests/infer.er").map_err(|errs| { let module = load_file("tests/infer.er").map_err(|errs| {
errs.fmt_all_stderr(); errs.write_all_stderr();
})?; })?;
let t = type_q("T"); let t = type_q("T");
let u = type_q("U"); let u = type_q("U");

View file

@ -148,10 +148,10 @@ impl Runnable for Transpiler {
path.set_extension("py"); path.set_extension("py");
let src = self.cfg.input.read(); let src = self.cfg.input.read();
let artifact = self.transpile(src, "exec").map_err(|eart| { let artifact = self.transpile(src, "exec").map_err(|eart| {
eart.warns.fmt_all_stderr(); eart.warns.write_all_stderr();
eart.errors eart.errors
})?; })?;
artifact.warns.fmt_all_stderr(); artifact.warns.write_all_stderr();
let mut f = File::create(path).unwrap(); let mut f = File::create(path).unwrap();
f.write_all(artifact.object.code.as_bytes()).unwrap(); f.write_all(artifact.object.code.as_bytes()).unwrap();
Ok(ExitStatus::compile_passed(artifact.warns.len())) Ok(ExitStatus::compile_passed(artifact.warns.len()))
@ -159,10 +159,10 @@ impl Runnable for Transpiler {
fn eval(&mut self, src: String) -> Result<String, CompileErrors> { fn eval(&mut self, src: String) -> Result<String, CompileErrors> {
let artifact = self.transpile(src, "eval").map_err(|eart| { let artifact = self.transpile(src, "eval").map_err(|eart| {
eart.warns.fmt_all_stderr(); eart.warns.write_all_stderr();
eart.errors eart.errors
})?; })?;
artifact.warns.fmt_all_stderr(); artifact.warns.write_all_stderr();
Ok(artifact.object.code) Ok(artifact.object.code)
} }
} }

View file

@ -444,6 +444,22 @@ impl SubrType {
|| self.return_t.has_qvar() || self.return_t.has_qvar()
} }
pub fn has_undoable_linked_var(&self) -> bool {
self.non_default_params
.iter()
.any(|pt| pt.typ().has_undoable_linked_var())
|| self
.var_params
.as_ref()
.map(|pt| pt.typ().has_undoable_linked_var())
.unwrap_or(false)
|| self
.default_params
.iter()
.any(|pt| pt.typ().has_undoable_linked_var())
|| self.return_t.has_undoable_linked_var()
}
pub fn typarams(&self) -> Vec<TyParam> { pub fn typarams(&self) -> Vec<TyParam> {
[ [
self.non_default_params self.non_default_params
@ -2450,6 +2466,58 @@ impl Type {
} }
} }
pub fn has_undoable_linked_var(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_undoable_linked() => true,
Self::FreeVar(fv) if fv.is_linked() => fv.crack().has_undoable_linked_var(),
Self::FreeVar(fv) => {
if let Some((sub, sup)) = fv.get_subsup() {
fv.dummy_link();
let res_sub = sub.has_undoable_linked_var();
let res_sup = sup.has_undoable_linked_var();
fv.undo();
res_sub || res_sup
} else {
let opt_t = fv.get_type();
opt_t.map_or(false, |t| t.has_undoable_linked_var())
}
}
Self::Ref(ty) => ty.has_undoable_linked_var(),
Self::RefMut { before, after } => {
before.has_undoable_linked_var()
|| after
.as_ref()
.map(|t| t.has_undoable_linked_var())
.unwrap_or(false)
}
Self::And(lhs, rhs) | Self::Or(lhs, rhs) => {
lhs.has_undoable_linked_var() || rhs.has_undoable_linked_var()
}
Self::Not(ty) => ty.has_undoable_linked_var(),
Self::Callable { param_ts, return_t } => {
param_ts.iter().any(|t| t.has_undoable_linked_var())
|| return_t.has_undoable_linked_var()
}
Self::Subr(subr) => subr.has_undoable_linked_var(),
Self::Quantified(quant) => quant.has_undoable_linked_var(),
Self::Record(r) => r.values().any(|t| t.has_undoable_linked_var()),
Self::Refinement(refine) => {
refine.t.has_undoable_linked_var() || refine.pred.has_undoable_linked_var()
}
Self::Poly { params, .. } => params.iter().any(|tp| tp.has_undoable_linked_var()),
Self::Proj { lhs, .. } => lhs.has_undoable_linked_var(),
Self::ProjCall { lhs, args, .. } => {
lhs.has_undoable_linked_var() || args.iter().any(|tp| tp.has_undoable_linked_var())
}
Self::Structural(ty) => ty.has_undoable_linked_var(),
Self::Guard(guard) => guard.to.has_undoable_linked_var(),
Self::Bounded { sub, sup } => {
sub.has_undoable_linked_var() || sup.has_undoable_linked_var()
}
_ => false,
}
}
pub fn has_no_qvar(&self) -> bool { pub fn has_no_qvar(&self) -> bool {
!self.has_qvar() !self.has_qvar()
} }

View file

@ -341,6 +341,21 @@ impl Predicate {
} }
} }
pub fn has_undoable_linked_var(&self) -> bool {
match self {
Self::Value(_) => false,
Self::Const(_) => false,
Self::Equal { rhs, .. }
| Self::GreaterEqual { rhs, .. }
| Self::LessEqual { rhs, .. }
| Self::NotEqual { rhs, .. } => rhs.has_undoable_linked_var(),
Self::Or(lhs, rhs) | Self::And(lhs, rhs) => {
lhs.has_undoable_linked_var() || rhs.has_undoable_linked_var()
}
Self::Not(pred) => pred.has_undoable_linked_var(),
}
}
pub fn min_max<'a>( pub fn min_max<'a>(
&'a self, &'a self,
min: Option<&'a TyParam>, min: Option<&'a TyParam>,

View file

@ -1062,6 +1062,29 @@ impl TyParam {
!self.has_unbound_var() !self.has_unbound_var()
} }
pub fn has_undoable_linked_var(&self) -> bool {
match self {
Self::FreeVar(fv) => fv.is_undoable_linked(),
Self::Type(t) => t.has_undoable_linked_var(),
Self::Proj { obj, .. } => obj.has_undoable_linked_var(),
Self::Array(ts) | Self::Tuple(ts) => ts.iter().any(|t| t.has_undoable_linked_var()),
Self::Set(ts) => ts.iter().any(|t| t.has_undoable_linked_var()),
Self::Dict(kv) => kv
.iter()
.any(|(k, v)| k.has_undoable_linked_var() || v.has_undoable_linked_var()),
Self::Record(rec) => rec.iter().any(|(_, v)| v.has_undoable_linked_var()),
Self::Lambda(lambda) => lambda.body.iter().any(|t| t.has_undoable_linked_var()),
Self::UnaryOp { val, .. } => val.has_undoable_linked_var(),
Self::BinOp { lhs, rhs, .. } => {
lhs.has_undoable_linked_var() || rhs.has_undoable_linked_var()
}
Self::App { args, .. } => args.iter().any(|p| p.has_undoable_linked_var()),
Self::Erased(t) => t.has_undoable_linked_var(),
Self::Value(ValueObj::Type(t)) => t.typ().has_undoable_linked_var(),
_ => false,
}
}
pub fn union_size(&self) -> usize { pub fn union_size(&self) -> usize {
match self { match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().union_size(), Self::FreeVar(fv) if fv.is_linked() => fv.crack().union_size(),

View file

@ -7,10 +7,10 @@ use std::hash::{Hash, Hasher};
use std::ops::Neg; use std::ops::Neg;
use std::sync::Arc; use std::sync::Arc;
use erg_common::config::Input;
use erg_common::dict::Dict; use erg_common::dict::Dict;
use erg_common::error::{ErrorCore, ErrorKind, Location}; use erg_common::error::{ErrorCore, ErrorKind, Location};
use erg_common::fresh::fresh_varname; use erg_common::fresh::fresh_varname;
use erg_common::io::Input;
use erg_common::python_util::PythonVersion; use erg_common::python_util::PythonVersion;
use erg_common::serialize::*; use erg_common::serialize::*;
use erg_common::set::Set; use erg_common::set::Set;

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use erg_common::error::Location; use erg_common::error::Location;
use erg_common::set::Set; use erg_common::set::Set;
use erg_common::Str; use erg_common::{switch_lang, Str};
use erg_parser::ast::DefId; use erg_parser::ast::DefId;
@ -39,6 +39,7 @@ use Mutability::*;
pub enum VarKind { pub enum VarKind {
Defined(DefId), Defined(DefId),
Declared, Declared,
InstanceAttr,
Parameter { Parameter {
def_id: DefId, def_id: DefId,
var: bool, var: bool,
@ -88,6 +89,38 @@ impl VarKind {
pub const fn is_builtin(&self) -> bool { pub const fn is_builtin(&self) -> bool {
matches!(self, Self::Builtin) matches!(self, Self::Builtin)
} }
pub const fn is_auto(&self) -> bool {
matches!(self, Self::Auto)
}
pub const fn is_instance_attr(&self) -> bool {
matches!(self, Self::InstanceAttr)
}
pub const fn display(&self) -> &'static str {
match self {
Self::Auto | Self::FixedAuto => switch_lang!(
"japanese" => "自動",
"simplified_chinese" => "自动",
"traditional_chinese" => "自動",
"english" => "auto",
),
Self::Builtin => switch_lang!(
"japanese" => "組み込み",
"simplified_chinese" => "内置",
"traditional_chinese" => "內置",
"english" => "builtin",
),
Self::InstanceAttr => switch_lang!(
"japanese" => "インスタンス",
"simplified_chinese" => "实例",
"traditional_chinese" => "實例",
"english" => "instance",
),
_ => "",
}
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -285,12 +318,11 @@ impl VarInfo {
} else { } else {
Mutability::Immutable Mutability::Immutable
}; };
let kind = VarKind::Declared;
Self::new( Self::new(
t, t,
muty, muty,
Visibility::new(field.vis, namespace), Visibility::new(field.vis, namespace),
kind, VarKind::InstanceAttr,
None, None,
impl_of, impl_of,
None, None,

View file

@ -1004,6 +1004,15 @@ impl_nested_display_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl_display_for_enum!(RecordAttrOrIdent; Attr, Ident); impl_display_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl_locational_for_enum!(RecordAttrOrIdent; Attr, Ident); impl_locational_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl RecordAttrOrIdent {
pub fn ident(&self) -> Option<&Identifier> {
match self {
Self::Attr(attr) => attr.sig.ident(),
Self::Ident(ident) => Some(ident),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct NormalSet { pub struct NormalSet {
pub l_brace: Token, pub l_brace: Token,

View file

@ -4,7 +4,7 @@ use erg_common::Str;
use crate::ast::AST; use crate::ast::AST;
use crate::desugar::Desugarer; use crate::desugar::Desugarer;
use crate::error::{ParserRunnerError, ParserRunnerErrors}; use crate::error::{CompleteArtifact, IncompleteArtifact, ParserRunnerError, ParserRunnerErrors};
use crate::parse::ParserRunner; use crate::parse::ParserRunner;
/// Summarize parsing and desugaring /// Summarize parsing and desugaring
@ -45,31 +45,55 @@ impl Runnable for ASTBuilder {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> { fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read(); let src = self.cfg_mut().input.read();
let ast = self.build(src)?; let artifact = self.build(src).map_err(|iart| iart.errors)?;
println!("{ast}"); println!("{}", artifact.ast);
Ok(ExitStatus::OK) Ok(ExitStatus::OK)
} }
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> { fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.build(src)?; let artifact = self.build(src).map_err(|iart| iart.errors)?;
Ok(format!("{ast}")) Ok(format!("{}", artifact.ast))
} }
} }
impl ASTBuilder { impl ASTBuilder {
pub fn build(&mut self, src: String) -> Result<AST, ParserRunnerErrors> { pub fn build(
let module = self.runner.parse(src)?; &mut self,
src: String,
) -> Result<
CompleteArtifact<AST, ParserRunnerErrors>,
IncompleteArtifact<AST, ParserRunnerErrors>,
> {
let name = Str::rc(self.runner.cfg().input.unescaped_filename());
let artifact = self
.runner
.parse(src)
.map_err(|iart| iart.map_mod(|module| AST::new(name.clone(), module)))?;
let mut desugarer = Desugarer::new(); let mut desugarer = Desugarer::new();
let module = desugarer.desugar(module); let module = desugarer.desugar(artifact.ast);
let name = self.runner.cfg().input.unescaped_filename(); let ast = AST::new(name, module);
let ast = AST::new(Str::rc(name), module); Ok(CompleteArtifact::new(
Ok(ast) ast,
ParserRunnerErrors::convert(self.input(), artifact.warns),
))
} }
pub fn build_without_desugaring(&mut self, src: String) -> Result<AST, ParserRunnerErrors> { pub fn build_without_desugaring(
let module = self.runner.parse(src)?; &mut self,
let name = self.runner.cfg().input.unescaped_filename(); src: String,
let ast = AST::new(Str::rc(name), module); ) -> Result<
Ok(ast) CompleteArtifact<AST, ParserRunnerErrors>,
IncompleteArtifact<AST, ParserRunnerErrors>,
> {
let name = Str::rc(self.runner.cfg().input.unescaped_filename());
let artifact = self
.runner
.parse(src)
.map_err(|iart| iart.map_mod(|module| AST::new(name.clone(), module)))?;
let ast = AST::new(name, artifact.ast);
Ok(CompleteArtifact::new(
ast,
ParserRunnerErrors::convert(self.input(), artifact.warns),
))
} }
} }

View file

@ -3,14 +3,15 @@
//! パーサーが出すエラーを定義 //! パーサーが出すエラーを定義
use std::fmt; use std::fmt;
use erg_common::config::Input;
use erg_common::error::{ use erg_common::error::{
ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage, ErrorCore, ErrorDisplay, ErrorKind::*, Location, MultiErrorDisplay, SubMessage,
}; };
use erg_common::style::{Attribute, Color, StyledStr, StyledStrings, THEME}; use erg_common::io::Input;
use erg_common::style::{Attribute, Color, StyledStr, StyledString, StyledStrings, THEME};
use erg_common::traits::Stream; use erg_common::traits::Stream;
use erg_common::{fmt_iter, fmt_vec_split_with, impl_display_and_error, impl_stream, switch_lang}; use erg_common::{fmt_iter, fmt_vec_split_with, impl_display_and_error, impl_stream, switch_lang};
use crate::ast::Module;
use crate::token::TokenKind; use crate::token::TokenKind;
#[derive(Debug)] #[derive(Debug)]
@ -50,6 +51,7 @@ impl fmt::Display for LexErrors {
impl std::error::Error for LexErrors {} impl std::error::Error for LexErrors {}
const ERR: Color = THEME.colors.error; const ERR: Color = THEME.colors.error;
const WARN: Color = THEME.colors.warning;
const HINT: Color = THEME.colors.hint; const HINT: Color = THEME.colors.hint;
#[cfg(not(feature = "pretty"))] #[cfg(not(feature = "pretty"))]
const ATTR: Attribute = Attribute::Bold; const ATTR: Attribute = Attribute::Bold;
@ -534,12 +536,30 @@ impl LexError {
); );
Self::syntax_error(errno, loc, msg, None) Self::syntax_error(errno, loc, msg, None)
} }
pub fn duplicate_elem_warning(errno: usize, loc: Location, elem: String) -> Self {
let elem = StyledString::new(elem, Some(WARN), Some(Attribute::Underline));
Self::new(ErrorCore::new(
vec![SubMessage::only_loc(loc)],
switch_lang!(
"japanese" => format!("重複する要素です: {elem}"),
"simplified_chinese" => format!("{elem}"),
"traditional_chinese" => format!("{elem}"),
"english" => format!("duplicated element: {elem}"),
),
errno,
SyntaxWarning,
loc,
))
}
} }
pub type LexResult<T> = Result<T, LexError>; pub type LexResult<T> = Result<T, LexError>;
pub type ParseError = LexError; pub type ParseError = LexError;
pub type ParseErrors = LexErrors; pub type ParseErrors = LexErrors;
pub type ParseWarning = LexError;
pub type ParseWarnings = LexErrors;
pub type ParseResult<T> = Result<T, ()>; pub type ParseResult<T> = Result<T, ()>;
#[derive(Debug)] #[derive(Debug)]
@ -618,4 +638,71 @@ pub type ParserRunnerResult<T> = Result<T, ParserRunnerError>;
pub type LexerRunnerError = ParserRunnerError; pub type LexerRunnerError = ParserRunnerError;
pub type LexerRunnerErrors = ParserRunnerErrors; pub type LexerRunnerErrors = ParserRunnerErrors;
pub type ParserRunnerWarning = ParserRunnerError;
pub type ParserRunnerWarnings = ParserRunnerErrors;
pub type LexerRunnerResult<T> = Result<T, LexerRunnerError>; pub type LexerRunnerResult<T> = Result<T, LexerRunnerError>;
#[derive(Debug)]
pub struct CompleteArtifact<A = Module, Es = ParseErrors> {
pub ast: A,
pub warns: Es,
}
impl<A, Es> CompleteArtifact<A, Es> {
pub fn new(ast: A, warns: Es) -> Self {
Self { ast, warns }
}
}
#[derive(Debug)]
pub struct IncompleteArtifact<A = Module, Es = ParseErrors> {
pub ast: Option<A>,
pub warns: Es,
pub errors: Es,
}
impl<A> From<ParserRunnerErrors> for IncompleteArtifact<A, ParserRunnerErrors> {
fn from(value: ParserRunnerErrors) -> IncompleteArtifact<A, ParserRunnerErrors> {
IncompleteArtifact::new(None, ParserRunnerErrors::empty(), value)
}
}
impl<A> From<LexErrors> for IncompleteArtifact<A, ParseErrors> {
fn from(value: LexErrors) -> IncompleteArtifact<A, ParseErrors> {
IncompleteArtifact::new(None, ParseErrors::empty(), value)
}
}
impl<A, Es> IncompleteArtifact<A, Es> {
pub fn new(ast: Option<A>, warns: Es, errors: Es) -> Self {
Self { ast, warns, errors }
}
pub fn map_errs<U>(self, f: impl Fn(Es) -> U) -> IncompleteArtifact<A, U> {
IncompleteArtifact {
ast: self.ast,
warns: f(self.warns),
errors: f(self.errors),
}
}
pub fn map_mod<U>(self, f: impl Fn(A) -> U) -> IncompleteArtifact<U, Es> {
IncompleteArtifact {
ast: self.ast.map(f),
warns: self.warns,
errors: self.errors,
}
}
}
#[derive(Debug)]
pub struct ErrorArtifact<Es = ParseErrors> {
pub warns: Es,
pub errors: Es,
}
impl<Es> ErrorArtifact<Es> {
pub fn new(warns: Es, errors: Es) -> ErrorArtifact<Es> {
Self { warns, errors }
}
}

View file

@ -6,7 +6,7 @@ use unicode_xid::UnicodeXID;
use erg_common::cache::CacheSet; use erg_common::cache::CacheSet;
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
use erg_common::config::Input; use erg_common::io::Input;
use erg_common::traits::DequeStream; use erg_common::traits::DequeStream;
use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang}; use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};

View file

@ -5,8 +5,8 @@
use std::mem; use std::mem;
use erg_common::config::ErgConfig; use erg_common::config::ErgConfig;
use erg_common::config::{Input, InputKind};
use erg_common::error::Location; use erg_common::error::Location;
use erg_common::io::{Input, InputKind};
use erg_common::set::Set as HashSet; use erg_common::set::Set as HashSet;
use erg_common::str::Str; use erg_common::str::Str;
use erg_common::traits::{DequeStream, ExitStatus, Locational, Runnable, Stream}; use erg_common::traits::{DequeStream, ExitStatus, Locational, Runnable, Stream};
@ -17,7 +17,10 @@ use erg_common::{
use crate::ast::*; use crate::ast::*;
use crate::desugar::Desugarer; use crate::desugar::Desugarer;
use crate::error::{ParseError, ParseErrors, ParseResult, ParserRunnerError, ParserRunnerErrors}; use crate::error::{
CompleteArtifact, IncompleteArtifact, ParseError, ParseErrors, ParseResult, ParserRunnerError,
ParserRunnerErrors,
};
use crate::lex::Lexer; use crate::lex::Lexer;
use crate::token::{Token, TokenCategory, TokenKind, TokenStream}; use crate::token::{Token, TokenCategory, TokenKind, TokenStream};
@ -96,13 +99,13 @@ macro_rules! expect_pop {
} }
pub trait Parsable { pub trait Parsable {
fn parse(code: String) -> Result<Module, ParseErrors>; fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParseErrors>>;
} }
pub struct SimpleParser {} pub struct SimpleParser {}
impl Parsable for SimpleParser { impl Parsable for SimpleParser {
fn parse(code: String) -> Result<Module, ParseErrors> { fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact> {
let ts = Lexer::from_str(code).lex()?; let ts = Lexer::from_str(code).lex()?;
let mut parser = Parser::new(ts); let mut parser = Parser::new(ts);
parser.parse() parser.parse()
@ -416,59 +419,77 @@ impl Runnable for ParserRunner {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> { fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read(); let src = self.cfg_mut().input.read();
let ast = self.parse(src)?; let artifact = self.parse(src).map_err(|iart| iart.errors)?;
println!("{ast}"); println!("{}", artifact.ast);
Ok(ExitStatus::OK) Ok(ExitStatus::OK)
} }
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> { fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.parse(src)?; let artifact = self.parse(src).map_err(|iart| iart.errors)?;
Ok(format!("{ast}")) Ok(format!("{}", artifact.ast))
} }
} }
impl ParserRunner { impl ParserRunner {
pub fn parse_token_stream(&mut self, ts: TokenStream) -> Result<Module, ParserRunnerErrors> { pub fn parse_token_stream(
&mut self,
ts: TokenStream,
) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParserRunnerErrors>> {
Parser::new(ts) Parser::new(ts)
.parse() .parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) .map_err(|iart| iart.map_errs(|errs| ParserRunnerErrors::convert(self.input(), errs)))
} }
pub fn parse(&mut self, src: String) -> Result<Module, ParserRunnerErrors> { pub fn parse(
&mut self,
src: String,
) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParserRunnerErrors>> {
let ts = Lexer::new(Input::new(InputKind::Str(src), self.cfg.input.id())) let ts = Lexer::new(Input::new(InputKind::Str(src), self.cfg.input.id()))
.lex() .lex()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?;
Parser::new(ts) Parser::new(ts)
.parse() .parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) .map_err(|iart| iart.map_errs(|errs| ParserRunnerErrors::convert(self.input(), errs)))
} }
} }
impl Parser { impl Parser {
pub fn parse(&mut self) -> Result<Module, ParseErrors> { pub fn parse(&mut self) -> Result<CompleteArtifact, IncompleteArtifact> {
if self.tokens.is_empty() { if self.tokens.is_empty() {
return Ok(Module::empty()); return Ok(CompleteArtifact::new(Module::empty(), ParseErrors::empty()));
} }
log!(info "the parsing process has started."); log!(info "the parsing process has started.");
log!(info "token stream: {}", self.tokens); log!(info "token stream: {}", self.tokens);
let module = match self.try_reduce_module() { let module = match self.try_reduce_module() {
Ok(module) => module, Ok(module) => module,
Err(_) => { Err(_) => {
return Err(mem::take(&mut self.errs)); return Err(IncompleteArtifact::new(
None,
mem::take(&mut self.warns),
mem::take(&mut self.errs),
));
} }
}; };
if !self.cur_is(EOF) { if !self.cur_is(EOF) {
let loc = self.peek().map(|t| t.loc()).unwrap_or_default(); let loc = self.peek().map(|t| t.loc()).unwrap_or_default();
self.errs self.errs
.push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); .push(ParseError::compiler_bug(0, loc, fn_name!(), line!()));
return Err(mem::take(&mut self.errs)); return Err(IncompleteArtifact::new(
Some(module),
mem::take(&mut self.warns),
mem::take(&mut self.errs),
));
} }
log!(info "the parsing process has completed (errs: {}).", self.errs.len()); log!(info "the parsing process has completed (errs: {}).", self.errs.len());
log!(info "AST:\n{module}"); log!(info "AST:\n{module}");
if self.errs.is_empty() { if self.errs.is_empty() {
Ok(module) Ok(CompleteArtifact::new(module, mem::take(&mut self.warns)))
} else { } else {
Err(mem::take(&mut self.errs)) Err(IncompleteArtifact::new(
Some(module),
mem::take(&mut self.warns),
mem::take(&mut self.errs),
))
} }
} }
@ -2839,6 +2860,17 @@ impl Parser {
})?; })?;
match next { match next {
Expr::Def(def) => { Expr::Def(def) => {
if attrs.iter().any(|attr| {
attr.ident()
.zip(def.sig.ident())
.is_some_and(|(l, r)| l == r)
}) {
self.warns.push(ParseError::duplicate_elem_warning(
line!() as usize,
def.sig.loc(),
def.sig.to_string(),
));
}
attrs.push(RecordAttrOrIdent::Attr(def)); attrs.push(RecordAttrOrIdent::Attr(def));
} }
Expr::Accessor(acc) => { Expr::Accessor(acc) => {

View file

@ -1,83 +1,97 @@
use erg_common::config::{ErgConfig, Input}; use erg_common::config::ErgConfig;
use erg_common::consts::DEBUG_MODE;
use erg_common::error::MultiErrorDisplay; use erg_common::error::MultiErrorDisplay;
use erg_common::io::Input;
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::traits::{Runnable, Stream}; use erg_common::traits::{Runnable, Stream};
use erg_parser::error::ParserRunnerErrors; use erg_parser::error::{ErrorArtifact, ParseWarnings, ParserRunnerErrors};
use erg_parser::lex::Lexer; use erg_parser::lex::Lexer;
use erg_parser::ParserRunner; use erg_parser::ParserRunner;
#[test] #[test]
fn parse_args() -> Result<(), ()> { fn parse_args() -> Result<(), ()> {
expect_success("tests/args.er") expect_success("tests/args.er", 0)
} }
#[test] #[test]
fn parse_containers() -> Result<(), ()> { fn parse_containers() -> Result<(), ()> {
expect_success("tests/containers.er") expect_success("tests/containers.er", 0)
} }
#[test] #[test]
fn parse_dependent() -> Result<(), ()> { fn parse_dependent() -> Result<(), ()> {
expect_success("tests/dependent.er") expect_success("tests/dependent.er", 0)
} }
#[test] #[test]
fn parse_fib() -> Result<(), ()> { fn parse_fib() -> Result<(), ()> {
expect_success("tests/fib.er") expect_success("tests/fib.er", 0)
} }
#[test] #[test]
fn parse_hello_world() -> Result<(), ()> { fn parse_hello_world() -> Result<(), ()> {
expect_success("tests/hello_world.er") expect_success("tests/hello_world.er", 0)
} }
#[test] #[test]
fn parse_simple_if() -> Result<(), ()> { fn parse_simple_if() -> Result<(), ()> {
expect_success("tests/simple_if.er") expect_success("tests/simple_if.er", 0)
} }
#[test] #[test]
fn parse_stream() -> Result<(), ()> { fn parse_stream() -> Result<(), ()> {
expect_success("tests/stream.er") expect_success("tests/stream.er", 0)
} }
#[test] #[test]
fn parse_test1_basic_syntax() -> Result<(), ()> { fn parse_test1_basic_syntax() -> Result<(), ()> {
expect_success("tests/test1_basic_syntax.er") expect_success("tests/test1_basic_syntax.er", 0)
} }
#[test] #[test]
fn parse_test2_advanced_syntax() -> Result<(), ()> { fn parse_test2_advanced_syntax() -> Result<(), ()> {
expect_success("tests/test2_advanced_syntax.er") expect_success("tests/test2_advanced_syntax.er", 0)
} }
#[test] #[test]
fn parse_stack() -> Result<(), ()> { fn parse_stack() -> Result<(), ()> {
expect_failure("tests/stack.er", 2) expect_failure("tests/stack.er", 0, 2)
} }
#[test] #[test]
fn parse_str_literal() -> Result<(), ()> { fn parse_str_literal() -> Result<(), ()> {
expect_failure("tests/failed_str_lit.er", 2) expect_failure("tests/failed_str_lit.er", 0, 2)
} }
#[test] #[test]
fn parse_invalid_chunk() -> Result<(), ()> { fn parse_invalid_chunk() -> Result<(), ()> {
expect_failure("tests/invalid_chunk.er", 62) expect_failure("tests/invalid_chunk.er", 0, 62)
} }
#[test] #[test]
fn parse_invalid_collections() -> Result<(), ()> { fn parse_invalid_collections() -> Result<(), ()> {
expect_failure("tests/invalid_collections.er", 29) expect_failure("tests/invalid_collections.er", 0, 29)
} }
#[test] #[test]
fn parse_invalid_class_definition() -> Result<(), ()> { fn parse_invalid_class_definition() -> Result<(), ()> {
expect_failure("tests/invalid_class_definition.er", 7) expect_failure("tests/invalid_class_definition.er", 0, 7)
} }
fn _parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErrors> { #[test]
fn exec_invalid_chunk_prs_err() -> Result<(), ()> {
expect_failure("tests/invalid_chunk.er", 0, 62)
}
#[test]
fn exec_warns() -> Result<(), ()> {
expect_success("tests/warns.er", 1)
}
fn _parse_test_from_code(
file_path: &'static str,
) -> Result<ParseWarnings, ErrorArtifact<ParserRunnerErrors>> {
let input = Input::file(file_path.into()); let input = Input::file(file_path.into());
let cfg = ErgConfig { let cfg = ErgConfig {
input: input.clone(), input: input.clone(),
@ -86,43 +100,81 @@ fn _parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErro
}; };
let lexer = Lexer::new(input.clone()); let lexer = Lexer::new(input.clone());
let mut parser = ParserRunner::new(cfg); let mut parser = ParserRunner::new(cfg);
match parser.parse_token_stream( match parser.parse_token_stream(lexer.lex().map_err(|errs| {
lexer ErrorArtifact::new(
.lex() ParserRunnerErrors::empty(),
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?, ParserRunnerErrors::convert(&input, errs),
) { )
Ok(module) => { })?) {
println!("{module}"); Ok(artifact) => {
Ok(()) if DEBUG_MODE {
println!("{}", artifact.ast);
} }
Err(e) => { Ok(artifact.warns)
e.fmt_all_stderr(); }
Err(e) Err(artifact) => {
if DEBUG_MODE {
artifact.warns.write_all_stderr();
artifact.errors.write_all_stderr();
}
Err(ErrorArtifact::new(artifact.warns, artifact.errors))
} }
} }
} }
fn parse_test_from_code(file_path: &'static str) -> Result<(), ParserRunnerErrors> { fn parse_test_from_code(
file_path: &'static str,
) -> Result<ParseWarnings, ErrorArtifact<ParserRunnerErrors>> {
exec_new_thread(move || _parse_test_from_code(file_path), file_path) exec_new_thread(move || _parse_test_from_code(file_path), file_path)
} }
fn expect_success(file_path: &'static str) -> Result<(), ()> { fn expect_success(file_path: &'static str, num_warns: usize) -> Result<(), ()> {
match parse_test_from_code(file_path) { match parse_test_from_code(file_path) {
Ok(_) => Ok(()), Ok(warns) => {
if warns.len() == num_warns {
Ok(())
} else {
println!(
"err: number of warnings is not {num_warns} but {}",
warns.len()
);
Err(())
}
}
Err(_) => Err(()), Err(_) => Err(()),
} }
} }
fn expect_failure(file_path: &'static str, errs_len: usize) -> Result<(), ()> { fn expect_failure(file_path: &'static str, num_warns: usize, num_errs: usize) -> Result<(), ()> {
match parse_test_from_code(file_path) { match parse_test_from_code(file_path) {
Ok(_) => Err(()), Ok(_) => Err(()),
Err(errs) => { Err(eart) => match (eart.errors.len() == num_errs, eart.warns.len() == num_warns) {
if errs.len() == errs_len { (true, true) => Ok(()),
Ok(()) (true, false) => {
} else { println!(
println!("err: error length is not {errs_len} but {}", errs.len()); "err: number of warnings is not {num_warns} but {}",
eart.warns.len()
);
Err(()) Err(())
} }
(false, true) => {
println!(
"err: number of errors is not {num_errs} but {}",
eart.errors.len()
);
Err(())
} }
(false, false) => {
println!(
"err: number of warnings is not {num_warns} but {}",
eart.warns.len()
);
println!(
"err: number of errors is not {num_errs} but {}",
eart.errors.len()
);
Err(())
}
},
} }
} }

View file

@ -1,6 +1,6 @@
use std::iter::Iterator; use std::iter::Iterator;
use erg_common::config::Input; use erg_common::io::Input;
// use erg_compiler::parser; // use erg_compiler::parser;

View file

@ -0,0 +1 @@
_ = { .foo = 1; .foo = 1 } # WARN

View file

@ -61,25 +61,45 @@ class C:
No syntax other than declarations and definitions (aliasing) are allowed in ``d.er``. No syntax other than declarations and definitions (aliasing) are allowed in ``d.er``.
Note that all Python functions can only be registered as procedures, and all classes as variable classes. ## Overloading
A special type that can be used only with Python typing is the overloaded type. This is a type that can accept multiple types.
```python ```python
foo = pyimport "foo" f: (Int -> Str) and (Str -> Int)
assert foo.bar!(1) in Int
``` ```
This ensures type safety by performing type checking at runtime. The checking mechanism generally works as follows. Overloaded types can be declared by taking a subroutine type intersection (`and`, not union `or`).
This allows you to declare a function whose return type depends on the type of its arguments.
```python ```python
decl_proc proc!: Proc, T = f(1): Str
x => f("1"): Int
assert x in T.Input
y = proc!(x)
assert y in T.Output
y
``` ```
This is a runtime overhead, so [a project to statically type analyze Python scripts with Erg's type system](https://github.com/mtshiba/pylyzer) is underway. The type decisions are collated from left to right, and the first match is applied.
Such polymorphism is called ad hoc polymorphism and is different from Erg's polymorphism, which uses type variables and trait bounds. Ad hoc polymorphism is generally discouraged, but it is a necessary because of its universal use in Python code.
Parameter types of overloaded types may be in a subtype relationship and may have different number of parameters, but they must not be of the same type, i.e. return type overload is not allowed.
```python
# OK
f: (Nat -> Str) and (Int -> Int)
f: ((Int, Int) -> Str) and (Int -> Int)
```
```python,compile_fail
# NG
f: (Int -> Str) and (Int -> Int)
```
## Notes
Currently, Erg unconditionally trusts the contents of type declarations. In other words, you can declare a variable of type `Str` even if it is actually a variable of type `Int`, or declare a subroutine as a function even if it has side effects, etc.
Also, it is troublesome that type declarations cannot be omitted even for trivial code, so the [Project for static type analysis of Python scripts with Erg's type system](https://github.com/mtshiba/pylyzer) is underway.
<p align='center'> <p align='center'>
<a href='./33_pipeline.md'>Previous</a> | <a href='./35_package_system.md'>Next</a> <a href='./33_pipeline.md'>Previous</a> | <a href='./35_package_system.md'>Next</a>

View file

@ -1,6 +1,6 @@
# イテレータ # イテレータ
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/17_iterator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/17_iterator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/18_iterator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/18_iterator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
イテレータは、コンテナの要素を取り出すためのオブジェクトです。 イテレータは、コンテナの要素を取り出すためのオブジェクトです。

View file

@ -1,6 +1,6 @@
# 可変性 # 可変性
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/18_mutability.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/18_mutability.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/19_mutability.md%26commit_hash%3Db80234b0663f57388f022b86f7c94a85b6250e9a)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/19_mutability.md&commit_hash=b80234b0663f57388f022b86f7c94a85b6250e9a)
すでに見たように、Ergの変数は全て不変です。しかし、Ergのオブジェクトには可変性という概念があります。 すでに見たように、Ergの変数は全て不変です。しかし、Ergのオブジェクトには可変性という概念があります。
以下のコードを例にします。 以下のコードを例にします。

View file

@ -1,6 +1,6 @@
# 所有権システム # 所有権システム
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/19_ownership.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/19_ownership.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/20_ownership.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/20_ownership.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
ErgはPythonをホスト言語にした言語であるため、メモリ管理の方法はPythonの処理系に依存しています。 ErgはPythonをホスト言語にした言語であるため、メモリ管理の方法はPythonの処理系に依存しています。
しかし、意味論的にはErgのメモリ管理はPythonのそれとは別物です。顕著な違いは、所有権システムと循環参照の禁止に現れています。 しかし、意味論的にはErgのメモリ管理はPythonのそれとは別物です。顕著な違いは、所有権システムと循環参照の禁止に現れています。

View file

@ -1,6 +1,6 @@
# 可視性 # 可視性
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/20_visibility.md%26commit_hash%3D5fe4ad12075d710910f75c40552b4db621904c57)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/20_visibility.md&commit_hash=5fe4ad12075d710910f75c40552b4db621904c57) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/21_visibility.md%26commit_hash%3D5fe4ad12075d710910f75c40552b4db621904c57)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/21_visibility.md&commit_hash=5fe4ad12075d710910f75c40552b4db621904c57)
Ergの変数には __可視性__ という概念が存在します。 Ergの変数には __可視性__ という概念が存在します。
今まで見てきた変数は全て __プライベート変数(非公開変数)__ と呼ばれます。これは、外部から不可視の変数です。 今まで見てきた変数は全て __プライベート変数(非公開変数)__ と呼ばれます。これは、外部から不可視の変数です。

View file

@ -1,6 +1,6 @@
# 命名規則 # 命名規則
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/21_naming_rule.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/21_naming_rule.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/22_naming_rule.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/22_naming_rule.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
変数を定数式として使いたい場合は、必ず大文字で始めます。二文字以降は小文字でもよいです。 変数を定数式として使いたい場合は、必ず大文字で始めます。二文字以降は小文字でもよいです。

View file

@ -1,6 +1,6 @@
# 無名関数 # 無名関数
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/22_lambda.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/22_lambda.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/23_lambda.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/23_lambda.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94)
無名関数は、関数オブジェクトを名付けずその場で生成するための文法です。 無名関数は、関数オブジェクトを名付けずその場で生成するための文法です。

View file

@ -1,6 +1,6 @@
# サブルーチンシグネチャ # サブルーチンシグネチャ
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/23_subroutine.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/23_subroutine.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/24_subroutine.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/24_subroutine.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
## 関数 ## 関数

View file

@ -1,6 +1,6 @@
# クロージャ # クロージャ
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/24_closure.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/24_closure.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/25_closure.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/25_closure.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
Ergのサブルーチンには、外部変数を捕捉する「クロージャ」という機能があります。 Ergのサブルーチンには、外部変数を捕捉する「クロージャ」という機能があります。

View file

@ -1,6 +1,6 @@
# モジュール # モジュール
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/25_module.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/25_module.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_module.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/26_module.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94)
Ergでは、ファイル自体を1つのレコードとみなすことができます[<sup id="f1">1</sup>](#1)。これをモジュールと呼びます。 Ergでは、ファイル自体を1つのレコードとみなすことができます[<sup id="f1">1</sup>](#1)。これをモジュールと呼びます。

View file

@ -1,6 +1,6 @@
# オブジェクトシステム # オブジェクトシステム
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_object_system.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/26_object_system.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/27_object_system.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/27_object_system.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50)
## オブジェクト(対象体) ## オブジェクト(対象体)

View file

@ -1,6 +1,6 @@
# パターンマッチ、論駁可能性 # パターンマッチ、論駁可能性
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_pattern_matching.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/27_pattern_matching.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/28_pattern_matching.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/28_pattern_matching.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
## Ergで使用可能なパターン ## Ergで使用可能なパターン

View file

@ -1,6 +1,6 @@
# 内包表記 # 内包表記
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/28_comprehension.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/28_comprehension.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/29_comprehension.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/29_comprehension.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
`[expr | (name <- iterable)+ (predicate)*]`で配列、 `[expr | (name <- iterable)+ (predicate)*]`で配列、
`{expr | (name <- iterable)+ (predicate)*}`でセット、 `{expr | (name <- iterable)+ (predicate)*}`でセット、

View file

@ -1,6 +1,6 @@
# 展開代入 # 展開代入
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/29_spread_syntax.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/29_spread_syntax.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/30_spread_syntax.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/30_spread_syntax.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50)
分解代入において、変数の前に`*`を置くと残りの要素を全てその変数に展開できます。これを展開代入と呼びます。 分解代入において、変数の前に`*`を置くと残りの要素を全てその変数に展開できます。これを展開代入と呼びます。

View file

@ -1,6 +1,6 @@
# デコレータ(修飾子) # デコレータ(修飾子)
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/30_decorator.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/30_decorator.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/31_decorator.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/31_decorator.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94)
デコレータは型や関数に特定の状態や振る舞いを追加したり明示するために使われます。 デコレータは型や関数に特定の状態や振る舞いを追加したり明示するために使われます。
デコレータの文法は以下の通りです。 デコレータの文法は以下の通りです。

View file

@ -1,6 +1,6 @@
# エラーハンドリング # エラーハンドリング
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/31_error_handling.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/31_error_handling.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_error_handling.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/32_error_handling.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
主にResult型を使用します。 主にResult型を使用します。
ErgではError型オブジェクトを捨てる(トップレベルで対応しない)とエラーが発生します。 ErgではError型オブジェクトを捨てる(トップレベルで対応しない)とエラーが発生します。

View file

@ -1,6 +1,6 @@
# パイプライン演算子 # パイプライン演算子
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_pipeline.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/32_pipeline.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/33_pipeline.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/33_pipeline.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94)
パイプライン演算子は、次のように使います。 パイプライン演算子は、次のように使います。

View file

@ -1,6 +1,6 @@
# Pythonとの連携 # Pythonとの連携
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_integration_with_Python.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/33_integration_with_Python.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/34_integration_with_Python.md%26commit_hash%3D0150fcc2b15ec6b4521de2b84fa42174547c2339)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/34_integration_with_Python.md&commit_hash=0150fcc2b15ec6b4521de2b84fa42174547c2339)
## Pythonへのexport ## Pythonへのexport
@ -64,25 +64,45 @@ class C:
`d.er`内では宣言と定義(エイリアシング)以外の構文は使えません。 `d.er`内では宣言と定義(エイリアシング)以外の構文は使えません。
Pythonの関数はすべてプロシージャとして、クラスはすべて可変クラスとしてしか登録できないことに注意してください。 ## オーバーロード
Pythonの型付けだけで使える特殊な型として、オーバーロード型があります。これは、複数の型を受け取ることができる型です。
```python ```python
foo = pyimport "foo" f: (Int -> Str) and (Str -> Int)
assert foo.bar!(1) in Int
``` ```
これは、実行時に型チェックを行うことで型安全性を担保しています。チェック機構は概念的には以下のように動作します。 オーバーロード型はサブルーチン型のintersection(`and`)を取ることで宣言できます。`or`ではないことに注意してください。
こうすると、引数の型によって戻り値の型が変わる関数を宣言できます。
```python ```python
decl_proc proc!: Proc, T = f(1): Str
x => f("1"): Int
assert x in T.Input
y = proc!(x)
assert y in T.Output
y
``` ```
これは実行時オーバーヘッドとなるので、[PythonスクリプトをErgの型システムで静的に型解析するプロジェクト](https://github.com/mtshiba/pylyzer)が進められています。 型判定は左から順に照合され、最初にマッチしたものが適用されます。
このような多相はアドホック多相と呼ばれ、型変数とトレイト境界を用いるErgの多相とは異なるものです。アドホック多相は一般的にはあまり推奨されませんが、Pythonのコードでは普遍的に使われているので、必要悪として存在します。
オーバーロード型の引数型は部分型関係にあっても良く、引数数が違っていいても良いですが、同じ型であってはいけません。すなわち、return type overloadingは許可されません。
```python
# OK
f: (Nat -> Str) and (Int -> Int)
f: ((Int, Int) -> Str) and (Int -> Int)
```
```python,compile_fail
# NG
f: (Int -> Str) and (Int -> Int)
```
## 注意点
現在のところ、Ergはこの型宣言の内容を無条件に信用します。すなわち、実際にはInt型の変数でもStr型として宣言する、副作用のあるサブルーチンでも関数として宣言する、などができてしまいます。
また、自明な型付けでも型宣言を省略できないのは面倒なので、[PythonスクリプトをErgの型システムで静的に型解析するプロジェクト](https://github.com/mtshiba/pylyzer)が進められています。
<p align='center'> <p align='center'>
<a href='./33_pipeline.md'>Previous</a> | <a href='./35_package_system.md'>Next</a> <a href='./33_pipeline.md'>Previous</a> | <a href='./35_package_system.md'>Next</a>

View file

@ -1,6 +1,6 @@
# パッケージシステム # パッケージシステム
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/34_package_system.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/34_package_system.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/35_package_system.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/35_package_system.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
Ergのパッケージはアプリケーションであるappパッケージとライブラリであるlibパッケージに大別できます。 Ergのパッケージはアプリケーションであるappパッケージとライブラリであるlibパッケージに大別できます。
appパッケージのエントリポイントは`src/app.er`です。`app.er`内に定義された`main`関数が実行されます。 appパッケージのエントリポイントは`src/app.er`です。`app.er`内に定義された`main`関数が実行されます。

View file

@ -1,6 +1,6 @@
# ジェネレータ # ジェネレータ
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/35_generator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/35_generator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/36_generator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/36_generator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61)
ジェネレータは、ブロック中で`yield!`プロシージャを使う特殊なプロシージャです。 ジェネレータは、ブロック中で`yield!`プロシージャを使う特殊なプロシージャです。

View file

@ -141,25 +141,25 @@ This file is generated automatically. If you want to edit this, edit [`doc/scrip
| [syntax/15_set.md](../EN/syntax/15_set.md) | [📝](../JA/syntax/15_set.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/15_set.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/15_set.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/15_set.md](../EN/syntax/15_set.md) | [📝](../JA/syntax/15_set.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/15_set.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/15_set.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/16_type.md](../EN/syntax/16_type.md) | [📝](../JA/syntax/16_type.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/16_type.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/16_type.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/16_type.md](../EN/syntax/16_type.md) | [📝](../JA/syntax/16_type.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/16_type.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/16_type.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/17_narrowing.md](../EN/syntax/17_narrowing.md) | [📝](../JA/syntax/17_narrowing.md) Badge not found | | [syntax/17_narrowing.md](../EN/syntax/17_narrowing.md) | [📝](../JA/syntax/17_narrowing.md) Badge not found |
| [syntax/18_iterator.md](../EN/syntax/18_iterator.md) | [📝](../JA/syntax/18_iterator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/17_iterator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/17_iterator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/18_iterator.md](../EN/syntax/18_iterator.md) | [📝](../JA/syntax/18_iterator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/18_iterator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/18_iterator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/19_mutability.md](../EN/syntax/19_mutability.md) | [📝](../JA/syntax/19_mutability.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/18_mutability.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/18_mutability.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/19_mutability.md](../EN/syntax/19_mutability.md) | [📝](../JA/syntax/19_mutability.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/19_mutability.md%26commit_hash%3Db80234b0663f57388f022b86f7c94a85b6250e9a)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/19_mutability.md&commit_hash=b80234b0663f57388f022b86f7c94a85b6250e9a) |
| [syntax/20_ownership.md](../EN/syntax/20_ownership.md) | [📝](../JA/syntax/20_ownership.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/19_ownership.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/19_ownership.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/20_ownership.md](../EN/syntax/20_ownership.md) | [📝](../JA/syntax/20_ownership.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/20_ownership.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/20_ownership.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/21_visibility.md](../EN/syntax/21_visibility.md) | [📝](../JA/syntax/21_visibility.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/20_visibility.md%26commit_hash%3D5fe4ad12075d710910f75c40552b4db621904c57)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/20_visibility.md&commit_hash=5fe4ad12075d710910f75c40552b4db621904c57) | | [syntax/21_visibility.md](../EN/syntax/21_visibility.md) | [📝](../JA/syntax/21_visibility.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/21_visibility.md%26commit_hash%3D5fe4ad12075d710910f75c40552b4db621904c57)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/21_visibility.md&commit_hash=5fe4ad12075d710910f75c40552b4db621904c57) |
| [syntax/22_naming_rule.md](../EN/syntax/22_naming_rule.md) | [📝](../JA/syntax/22_naming_rule.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/21_naming_rule.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/21_naming_rule.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/22_naming_rule.md](../EN/syntax/22_naming_rule.md) | [📝](../JA/syntax/22_naming_rule.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/22_naming_rule.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/22_naming_rule.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/23_lambda.md](../EN/syntax/23_lambda.md) | [📝](../JA/syntax/23_lambda.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/22_lambda.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/22_lambda.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) | | [syntax/23_lambda.md](../EN/syntax/23_lambda.md) | [📝](../JA/syntax/23_lambda.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/23_lambda.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/23_lambda.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) |
| [syntax/24_subroutine.md](../EN/syntax/24_subroutine.md) | [📝](../JA/syntax/24_subroutine.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/23_subroutine.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/23_subroutine.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/24_subroutine.md](../EN/syntax/24_subroutine.md) | [📝](../JA/syntax/24_subroutine.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/24_subroutine.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/24_subroutine.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/25_closure.md](../EN/syntax/25_closure.md) | [📝](../JA/syntax/25_closure.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/24_closure.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/24_closure.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/25_closure.md](../EN/syntax/25_closure.md) | [📝](../JA/syntax/25_closure.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/25_closure.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/25_closure.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/26_module.md](../EN/syntax/26_module.md) | [📝](../JA/syntax/26_module.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/25_module.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/25_module.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) | | [syntax/26_module.md](../EN/syntax/26_module.md) | [📝](../JA/syntax/26_module.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_module.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/26_module.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) |
| [syntax/27_object_system.md](../EN/syntax/27_object_system.md) | [📝](../JA/syntax/27_object_system.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_object_system.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/26_object_system.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) | | [syntax/27_object_system.md](../EN/syntax/27_object_system.md) | [📝](../JA/syntax/27_object_system.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/27_object_system.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/27_object_system.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) |
| [syntax/28_pattern_matching.md](../EN/syntax/28_pattern_matching.md) | [📝](../JA/syntax/28_pattern_matching.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/26_pattern_matching.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/27_pattern_matching.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/28_pattern_matching.md](../EN/syntax/28_pattern_matching.md) | [📝](../JA/syntax/28_pattern_matching.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/28_pattern_matching.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/28_pattern_matching.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/29_comprehension.md](../EN/syntax/29_comprehension.md) | [📝](../JA/syntax/29_comprehension.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/28_comprehension.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/28_comprehension.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/29_comprehension.md](../EN/syntax/29_comprehension.md) | [📝](../JA/syntax/29_comprehension.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/29_comprehension.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/29_comprehension.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/30_spread_syntax.md](../EN/syntax/30_spread_syntax.md) | [📝](../JA/syntax/30_spread_syntax.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/29_spread_syntax.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/29_spread_syntax.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) | | [syntax/30_spread_syntax.md](../EN/syntax/30_spread_syntax.md) | [📝](../JA/syntax/30_spread_syntax.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/30_spread_syntax.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/30_spread_syntax.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) |
| [syntax/31_decorator.md](../EN/syntax/31_decorator.md) | [📝](../JA/syntax/31_decorator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/30_decorator.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/30_decorator.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) | | [syntax/31_decorator.md](../EN/syntax/31_decorator.md) | [📝](../JA/syntax/31_decorator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/31_decorator.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/31_decorator.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) |
| [syntax/32_error_handling.md](../EN/syntax/32_error_handling.md) | [📝](../JA/syntax/32_error_handling.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/31_error_handling.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/31_error_handling.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/32_error_handling.md](../EN/syntax/32_error_handling.md) | [📝](../JA/syntax/32_error_handling.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_error_handling.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/32_error_handling.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/33_pipeline.md](../EN/syntax/33_pipeline.md) | [📝](../JA/syntax/33_pipeline.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_pipeline.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/32_pipeline.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) | | [syntax/33_pipeline.md](../EN/syntax/33_pipeline.md) | [📝](../JA/syntax/33_pipeline.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/33_pipeline.md%26commit_hash%3Dc8932f8fd75cc86f67421bb6b160fffaf7acdd94)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/33_pipeline.md&commit_hash=c8932f8fd75cc86f67421bb6b160fffaf7acdd94) |
| [syntax/34_integration_with_Python.md](../EN/syntax/34_integration_with_Python.md) | [📝](../JA/syntax/34_integration_with_Python.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/32_integration_with_Python.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/33_integration_with_Python.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/34_integration_with_Python.md](../EN/syntax/34_integration_with_Python.md) | [📝](../JA/syntax/34_integration_with_Python.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/34_integration_with_Python.md%26commit_hash%3D0150fcc2b15ec6b4521de2b84fa42174547c2339)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/34_integration_with_Python.md&commit_hash=0150fcc2b15ec6b4521de2b84fa42174547c2339) |
| [syntax/35_package_system.md](../EN/syntax/35_package_system.md) | [📝](../JA/syntax/35_package_system.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/34_package_system.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/34_package_system.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/35_package_system.md](../EN/syntax/35_package_system.md) | [📝](../JA/syntax/35_package_system.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/35_package_system.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/35_package_system.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/36_generator.md](../EN/syntax/36_generator.md) | [📝](../JA/syntax/36_generator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/35_generator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/35_generator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) | | [syntax/36_generator.md](../EN/syntax/36_generator.md) | [📝](../JA/syntax/36_generator.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/36_generator.md%26commit_hash%3De959b3e54bfa8cee4929743b0193a129e7525c61)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/36_generator.md&commit_hash=e959b3e54bfa8cee4929743b0193a129e7525c61) |
| [syntax/SUMMARY.md](../EN/syntax/SUMMARY.md) | [📝](../JA/syntax/SUMMARY.md) Badge not found | | [syntax/SUMMARY.md](../EN/syntax/SUMMARY.md) | [📝](../JA/syntax/SUMMARY.md) Badge not found |
| [syntax/grammar.md](../EN/syntax/grammar.md) | [📝](../JA/syntax/grammar.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/grammar.md%26commit_hash%3Df09ef75b8b7bb86f892f224db4392c7c340b1147)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/grammar.md&commit_hash=f09ef75b8b7bb86f892f224db4392c7c340b1147) | | [syntax/grammar.md](../EN/syntax/grammar.md) | [📝](../JA/syntax/grammar.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/grammar.md%26commit_hash%3Df09ef75b8b7bb86f892f224db4392c7c340b1147)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/grammar.md&commit_hash=f09ef75b8b7bb86f892f224db4392c7c340b1147) |
| [syntax/indexes.md](../EN/syntax/indexes.md) | [📝](../JA/syntax/indexes.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/indexes.md%26commit_hash%3D96b113c47ec6ca7ad91a6b486d55758de00d557d)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/indexes.md&commit_hash=96b113c47ec6ca7ad91a6b486d55758de00d557d) | | [syntax/indexes.md](../EN/syntax/indexes.md) | [📝](../JA/syntax/indexes.md) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/syntax/indexes.md%26commit_hash%3D96b113c47ec6ca7ad91a6b486d55758de00d557d)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/syntax/indexes.md&commit_hash=96b113c47ec6ca7ad91a6b486d55758de00d557d) |

View file

@ -287,11 +287,16 @@ impl Runnable for DummyVM {
.compiler .compiler
.compile_and_dump_as_pyc(&filename, src, "exec") .compile_and_dump_as_pyc(&filename, src, "exec")
.map_err(|eart| { .map_err(|eart| {
eart.warns.fmt_all_stderr(); eart.warns.write_all_to(&mut self.cfg_mut().output);
eart.errors eart.errors
})?; })?;
warns.fmt_all_stderr(); warns.write_all_to(&mut self.cfg_mut().output);
let code = exec_pyc(&filename, self.cfg().py_command, &self.cfg().runtime_args); let code = exec_pyc(
&filename,
self.cfg().py_command,
&self.cfg().runtime_args,
self.cfg().output.clone(),
);
remove_file(&filename).unwrap(); remove_file(&filename).unwrap();
Ok(ExitStatus::new(code.unwrap_or(1), warns.len(), 0)) Ok(ExitStatus::new(code.unwrap_or(1), warns.len(), 0))
} }

View file

@ -1,8 +1,10 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::path::PathBuf; use std::path::PathBuf;
use erg_common::config::{DummyStdin, ErgConfig, Input}; use erg_common::config::ErgConfig;
use erg_common::consts::DEBUG_MODE;
use erg_common::error::MultiErrorDisplay; use erg_common::error::MultiErrorDisplay;
use erg_common::io::{DummyStdin, Input, Output};
use erg_common::python_util::PythonVersion; use erg_common::python_util::PythonVersion;
use erg_common::spawn::exec_new_thread; use erg_common::spawn::exec_new_thread;
use erg_common::style::{colors::DEBUG_MAIN, RESET}; use erg_common::style::{colors::DEBUG_MAIN, RESET};
@ -21,8 +23,10 @@ pub(crate) fn expect_repl_success(name: &'static str, lines: Vec<String>) -> Res
Err(()) Err(())
} }
Err(errs) => { Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors"); println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(()) Err(())
} }
} }
@ -46,8 +50,10 @@ pub(crate) fn expect_success(file_path: &'static str, num_warns: usize) -> Resul
Err(()) Err(())
} }
Err(errs) => { Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors"); println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(()) Err(())
} }
} }
@ -71,8 +77,10 @@ pub(crate) fn expect_compile_success(file_path: &'static str, num_warns: usize)
Err(()) Err(())
} }
Err(errs) => { Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors"); println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(()) Err(())
} }
} }
@ -97,8 +105,10 @@ pub(crate) fn expect_repl_failure(
} }
} }
Err(errs) => { Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors"); println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(()) Err(())
} }
} }
@ -119,8 +129,10 @@ pub(crate) fn expect_end_with(file_path: &'static str, code: i32) -> Result<(),
} }
} }
Err(errs) => { Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should end with {code}, but got compile errors"); println!("err: should end with {code}, but got compile errors");
errs.fmt_all_stderr();
Err(()) Err(())
} }
} }
@ -148,7 +160,9 @@ pub(crate) fn expect_failure(
} }
} }
Err(errs) => { Err(errs) => {
errs.fmt_all_stderr(); if DEBUG_MODE {
errs.write_all_stderr();
}
if errs.len() == num_errs { if errs.len() == num_errs {
Ok(()) Ok(())
} else { } else {
@ -184,7 +198,12 @@ fn set_cfg(mut cfg: ErgConfig) -> ErgConfig {
/// To execute on other versions, change the version and magic number. /// To execute on other versions, change the version and magic number.
fn _exec_file(file_path: &'static str) -> Result<ExitStatus, CompileErrors> { fn _exec_file(file_path: &'static str) -> Result<ExitStatus, CompileErrors> {
println!("{DEBUG_MAIN}[test] exec {file_path}{RESET}"); println!("{DEBUG_MAIN}[test] exec {file_path}{RESET}");
let cfg = ErgConfig::with_main_path(PathBuf::from(file_path)); let mut cfg = ErgConfig::with_main_path(PathBuf::from(file_path));
cfg.output = if DEBUG_MODE {
Output::stdout()
} else {
Output::Null
};
let mut vm = DummyVM::new(set_cfg(cfg)); let mut vm = DummyVM::new(set_cfg(cfg));
vm.exec() vm.exec()
} }

View file

@ -10,7 +10,7 @@ use erg_compiler::Transpiler;
fn test_vm_embedding() -> Result<(), ()> { fn test_vm_embedding() -> Result<(), ()> {
let mut vm = DummyVM::default(); let mut vm = DummyVM::default();
vm.eval("print! \"Hello, world!\"".into()).map_err(|es| { vm.eval("print! \"Hello, world!\"".into()).map_err(|es| {
es.fmt_all_stderr(); es.write_all_stderr();
})?; })?;
vm.eval("prin \"Hello, world!\"".into()) vm.eval("prin \"Hello, world!\"".into())
.expect_err("should err"); .expect_err("should err");
@ -23,7 +23,7 @@ fn test_transpiler_embedding() -> Result<(), ()> {
let res = trans let res = trans
.transpile("print!(\"\")".into(), "exec") .transpile("print!(\"\")".into(), "exec")
.map_err(|es| { .map_err(|es| {
es.errors.fmt_all_stderr(); es.errors.write_all_stderr();
})?; })?;
assert!(res.object.code.ends_with("(print)(Str(\"\"),)\n")); assert!(res.object.code.ends_with("(print)(Str(\"\"),)\n"));
Ok(()) Ok(())

View file

@ -0,0 +1,9 @@
C = Class { .x = Int; .y = Int }
C.
x = "aa"
_: Str = C.x
_ = C.y # ERR
c = C.new({.x = 1; .y = 2})
_: Int = c.x
_: Int = c.y

View file

@ -70,3 +70,9 @@ _: Array!({"a", "b"}, 2) = !["a", "b"] # OK
_: Array!({"a", "b", "c"}, 2) = !["a", "b"] # OK _: Array!({"a", "b", "c"}, 2) = !["a", "b"] # OK
_: Array!({"a", "c"}, 2) = !["a", "b"] # ERR _: Array!({"a", "c"}, 2) = !["a", "b"] # ERR
_: Array!({"a"}, 2) = !["a", "b"] # ERR _: Array!({"a"}, 2) = !["a", "b"] # ERR
ii _: Iterable(Iterable(Str)) = None
ii [1] # ERR
ii [[1]] # ERR
ii [["a"]]
ii ["aaa"] # Str <: Iterable Str

View file

@ -4,3 +4,7 @@ add|R: Type, A <: Add(R)|(x: A, y: R): A.Output = x + y
f _: Tuple([{B: Bool | B == False} or Str, Int]) = None f _: Tuple([{B: Bool | B == False} or Str, Int]) = None
g _: Tuple([]) = None g _: Tuple([]) = None
_: Iterable(Array(Int, _)) = [[1]]
_: Array(Int, _) = [1]
_: Iterable(Dict({Str: Int})) = [{"a": 1}]

View file

@ -6,6 +6,9 @@ assert x == 3
g _: Int = None g _: Int = None
g 10 g 10
h _: Int, _: Int = None
h 10, 20
#[ #[
and: [Bool; 2] -> Bool and: [Bool; 2] -> Bool
and [True, t] = t and [True, t] = t

View file

@ -273,6 +273,11 @@ fn exec_assert_cast() -> Result<(), ()> {
expect_failure("examples/assert_cast.er", 0, 3) expect_failure("examples/assert_cast.er", 0, 3)
} }
#[test]
fn exec_class_attr_err() -> Result<(), ()> {
expect_failure("tests/should_err/class_attr.er", 1, 1)
}
#[test] #[test]
fn exec_collection_err() -> Result<(), ()> { fn exec_collection_err() -> Result<(), ()> {
expect_failure("tests/should_err/collection.er", 0, 4) expect_failure("tests/should_err/collection.er", 0, 4)
@ -345,7 +350,7 @@ fn exec_structural_err() -> Result<(), ()> {
#[test] #[test]
fn exec_subtyping_err() -> Result<(), ()> { fn exec_subtyping_err() -> Result<(), ()> {
expect_failure("tests/should_err/subtyping.er", 0, 15) expect_failure("tests/should_err/subtyping.er", 0, 17)
} }
#[test] #[test]