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

View file

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

View file

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

View file

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

View file

@ -19,7 +19,7 @@ use crate::util::{self, NormalizedUrl};
impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
pub(crate) fn get_ast(&self, uri: &NormalizedUrl) -> Option<Module> {
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>>(

View file

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

View file

@ -2,24 +2,18 @@
//!
//! コマンドオプション(パーサー)を定義する
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs::File;
use std::io::{stdin, BufRead, BufReader, IsTerminal, Read, Write};
use std::path::{Path, PathBuf};
use std::io::{stdin, IsTerminal, Read};
use std::path::PathBuf;
use std::process;
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::io::{Input, Output};
use crate::levenshtein::get_similar_name;
use crate::pathutil::add_postfix_foreach;
use crate::python_util::{detect_magic_number, get_python_version, get_sys_path, PythonVersion};
use crate::random::random;
use crate::normalize_path;
use crate::python_util::{detect_magic_number, get_python_version, PythonVersion};
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)]
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)]
pub struct ErgConfig {
pub mode: ErgMode,
@ -759,7 +88,8 @@ pub struct ErgConfig {
pub quiet_repl: bool,
pub show_type: bool,
pub input: Input,
pub output_dir: Option<&'static str>,
pub output: Output,
pub dist_dir: Option<&'static str>,
/// module name to be executed
pub module: &'static str,
/// verbosity level for system messages.
@ -786,7 +116,8 @@ impl Default for ErgConfig {
quiet_repl: false,
show_type: false,
input: Input::repl(),
output_dir: None,
output: Output::stdout(),
dist_dir: None,
module: "<module>",
verbose: 1,
ps1: ">>> ",
@ -820,7 +151,7 @@ impl ErgConfig {
}
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()))
} else {
self.input.full_path()
@ -828,7 +159,7 @@ impl ErgConfig {
}
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())
} else {
self.input.filename()
@ -948,7 +279,7 @@ impl ErgConfig {
.next()
.expect("the value of `--output-dir` is not passed")
.into_boxed_str();
cfg.output_dir = Some(Box::leak(output_dir));
cfg.dist_dir = Some(Box::leak(output_dir));
}
"--py-command" | "--python-command" => {
let py_command = args

View file

@ -5,7 +5,7 @@ use std::cmp::{self, Ordering};
use std::fmt;
use std::io::{stderr, BufWriter, Write as _};
use crate::config::{Input, InputKind};
use crate::io::{Input, InputKind};
use crate::style::Attribute;
use crate::style::Characters;
use crate::style::Color;
@ -985,12 +985,18 @@ macro_rules! impl_display_and_error {
}
pub trait MultiErrorDisplay<Item: ErrorDisplay>: Stream<Item> {
fn fmt_all_stderr(&self) {
fn write_all_stderr(&self) {
for err in self.iter() {
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 {
for err in self.iter() {
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 fxhash;
pub mod help_messages;
pub mod io;
pub mod lang;
pub mod levenshtein;
pub mod macros;

View file

@ -3,7 +3,7 @@
//! CPythonを呼び出すためのユーティリティー
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::{Command, Stdio};
use crate::fn_name_full;
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)
pub fn exec_pyc<S: Into<String>>(
pub fn exec_pyc<S: Into<String>, T: Into<Stdio>>(
file: S,
py_command: Option<&str>,
argv: &[&'static str],
stdout: T,
) -> Option<i32> {
let command = py_command
.map(ToString::to_string)
@ -741,6 +742,7 @@ pub fn exec_pyc<S: Into<String>>(
.arg(command)
.arg(&file.into())
.args(argv)
.stdout(stdout)
.spawn()
.expect("cannot execute python")
} else {
@ -748,6 +750,7 @@ pub fn exec_pyc<S: Into<String>>(
Command::new("sh")
.arg("-c")
.arg(exec_command)
.stdout(stdout)
.spawn()
.expect("cannot execute python")
};

View file

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

View file

@ -9,9 +9,10 @@ use std::mem;
use std::process;
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::error::{ErrorDisplay, ErrorKind, Location, MultiErrorDisplay};
use crate::io::{Input, InputKind};
use crate::{addr_eq, chomp, log, switch_unreachable};
pub trait DequeStream<T>: Sized {
@ -249,6 +250,10 @@ pub trait Stream<T>: Sized {
{
self.ref_mut_payload().extend(iter);
}
fn split_off(&mut self, at: usize) -> Vec<T> {
self.ref_mut_payload().split_off(at)
}
}
#[macro_export]
@ -792,7 +797,7 @@ pub trait Runnable: Sized + Default {
instance.quit_successfully(output);
}
num_errors += errs.len();
errs.fmt_all_stderr();
errs.write_all_stderr();
}
}
instance.input().set_block_begin();
@ -916,7 +921,7 @@ pub trait Runnable: Sized + Default {
return ExitStatus::new(0, 0, num_errors);
}
num_errors += errs.len();
errs.fmt_all_stderr();
errs.write_all_stderr();
}
}
instance.input().set_block_begin();
@ -931,7 +936,7 @@ pub trait Runnable: Sized + Default {
Ok(status) => status,
Err(errs) => {
num_errors += errs.len();
errs.fmt_all_stderr();
errs.write_all_stderr();
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::context::{Context, ContextProvider, ModuleContext};
use crate::effectcheck::SideEffectChecker;
use crate::error::{CompileError, CompileErrors};
use crate::error::{CompileError, CompileErrors, LowerWarnings};
use crate::lower::ASTLowerer;
use crate::module::SharedCompilerResource;
use crate::ownercheck::OwnershipChecker;
@ -66,18 +66,26 @@ impl Runnable for HIRBuilder {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(self.cfg_mut().input.read())?;
let artifact = self.check(ast, "exec").map_err(|arti| arti.errors)?;
artifact.warns.fmt_all_stderr();
let artifact = builder
.build(self.cfg_mut().input.read())
.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);
Ok(ExitStatus::compile_passed(artifact.warns.len()))
}
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
let mut builder = ASTBuilder::new(self.cfg().copy());
let ast = builder.build(src)?;
let artifact = self.check(ast, "eval").map_err(|arti| arti.errors)?;
artifact.warns.fmt_all_stderr();
let artifact = builder.build(src).map_err(|arti| arti.errors)?;
artifact.warns.write_all_stderr();
let artifact = self
.check(artifact.ast, "eval")
.map_err(|arti| arti.errors)?;
artifact.warns.write_all_stderr();
Ok(artifact.object.to_string())
}
}
@ -148,10 +156,13 @@ impl HIRBuilder {
mode: &str,
) -> Result<CompleteArtifact, IncompleteArtifact> {
let mut ast_builder = ASTBuilder::new(self.cfg().copy());
let ast = ast_builder.build(src).map_err(|errs| {
IncompleteArtifact::new(None, CompileErrors::from(errs), CompileErrors::empty())
})?;
self.check(ast, mode)
let artifact = ast_builder
.build(src)
.map_err(|iart| IncompleteArtifact::new(None, iart.errors.into(), iart.warns.into()))?;
self.lowerer
.warns
.extend(LowerWarnings::from(artifact.warns));
self.check(artifact.ast, mode)
}
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::value::GenTypeObj;
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::error::{ErrorDisplay, Location};
use erg_common::io::Input;
use erg_common::opcode::{CommonOpcode, CompareOp};
use erg_common::opcode308::Opcode308;
use erg_common::opcode310::Opcode310;
@ -646,8 +647,8 @@ impl PyCodeGenerator {
}
StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind {
Name => LOAD_NAME as u8,
Attr => LOAD_ATTR as u8,
Method => LOAD_METHOD as u8,
UnboundAttr => LOAD_ATTR as u8,
BoundAttr => LOAD_METHOD as u8,
},
}
}
@ -669,9 +670,9 @@ impl PyCodeGenerator {
StoreLoadKind::Local | StoreLoadKind::LocalConst => {
match acc_kind {
Name => STORE_NAME as u8,
Attr => STORE_ATTR as u8,
UnboundAttr => STORE_ATTR as u8,
// 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!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Attr)
.local_search(&escaped, UnboundAttr)
.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_arg(name.idx);
if self.py_version.minor >= Some(11) {
@ -809,9 +810,9 @@ impl PyCodeGenerator {
}
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Method)
.local_search(&escaped, BoundAttr)
.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_arg(name.idx);
if self.py_version.minor >= Some(11) {
@ -866,7 +867,7 @@ impl PyCodeGenerator {
}
Accessor::Attr(attr) => {
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);
} else {
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_arg(argc);
@ -2188,7 +2189,7 @@ impl PyCodeGenerator {
let is_py_api = method_name.is_py_api();
self.emit_expr(obj);
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) {
@ -3121,7 +3122,7 @@ impl PyCodeGenerator {
self.emit_load_name_instr(Identifier::private("#path"));
self.emit_load_method_instr(Identifier::public("append"));
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.emit_pop_top();
let erg_std_mod = Identifier::public("_erg_std_prelude");

View file

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

View file

@ -301,6 +301,13 @@ impl Context {
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() {
// Not `supertype_of` (only structures are compared)
@ -459,8 +466,8 @@ impl Context {
// => ?P.undoable_link(Int)
// => Mul Int :> Int
(FreeVar(lfv), rhs) => {
if let FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } = &*lfv.borrow() {
return self.supertype_of(t, rhs);
if let Some(t) = lfv.get_linked() {
return self.supertype_of(&t, rhs);
}
if let Some((_sub, sup)) = lfv.get_subsup() {
lfv.undoable_link(rhs);
@ -482,8 +489,8 @@ impl Context {
}
}
(lhs, FreeVar(rfv)) => {
if let FreeKind::Linked(t) | FreeKind::UndoableLinked { t, .. } = &*rfv.borrow() {
return self.supertype_of(lhs, t);
if let Some(t) = rfv.get_linked() {
return self.supertype_of(lhs, &t);
}
if let Some((sub, _sup)) = rfv.get_subsup() {
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(sub_p.clone()),
) {
(Ok(sup), Ok(sub)) => {
return match variance {
Variance::Contravariant => self.subtype_of(&sup, &sub),
Variance::Covariant => self.supertype_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)
}
}

View file

@ -1338,6 +1338,11 @@ impl Context {
TyParam::FreeVar(fv) if fv.is_linked() => self.convert_tp_into_type(fv.crack().clone()),
TyParam::Type(t) => Ok(t.as_ref().clone()),
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::Value(v) => self.convert_value_into_type(v).map_err(TyParam::Value),
// TODO: Dict, Set
@ -1650,6 +1655,20 @@ impl Context {
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<()> {
match qtp {
TyParam::FreeVar(ref fv) if fv.is_generalized() => {
@ -1693,6 +1712,49 @@ impl Context {
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) {
for tp in substituted_q.typarams().into_iter() {
match tp {

View file

@ -303,7 +303,9 @@ pub(crate) fn sub_tpdict_get<'d>(
let mut matches = vec![];
for (k, v) in dict.iter() {
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));
}
(_, _) if key == k => {

View file

@ -2,9 +2,9 @@
use std::option::Option; // conflicting to Type::Option
use std::path::{Path, PathBuf};
use erg_common::config::Input;
use erg_common::consts::{ERG_MODE, PYTHON_MODE};
use erg_common::error::{ErrorCore, Location, SubMessage};
use erg_common::io::Input;
use erg_common::levenshtein;
use erg_common::set::Set;
use erg_common::traits::{Locational, NoTypeDisplay, Stream};
@ -420,7 +420,7 @@ impl Context {
) -> Triple<VarInfo, TyCheckError> {
if let Some(vi) = self.get_current_scope_var(&ident.name) {
match self.validate_visibility(ident, vi, input, namespace) {
Ok(()) => {
Ok(()) if acc_kind.matches(vi) => {
return Triple::Ok(vi.clone());
}
Err(err) => {
@ -428,6 +428,7 @@ impl Context {
return Triple::Err(err);
}
}
_ => {}
}
} else if let Some((name, _vi)) = self
.future_defined_locals
@ -453,6 +454,17 @@ impl Context {
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 let Some(parent) = self.get_outer().or_else(|| self.get_builtins()) {
return parent.rec_get_var_info(ident, acc_kind, input, namespace);
@ -468,7 +480,7 @@ impl Context {
) -> Option<&mut VarInfo> {
if let Some(vi) = self.get_current_scope_var(&ident.name) {
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();
return Some(vi);
}
@ -477,6 +489,7 @@ impl Context {
return None;
}
}
_ => {}
}
}
if acc_kind.is_local() {
@ -500,7 +513,7 @@ impl Context {
.or_else(|| self.future_defined_locals.get(&ident.inspect()[..]))
{
match self.validate_visibility(ident, vi, input, namespace) {
Ok(()) => {
Ok(()) if acc_kind.matches(vi) => {
return Triple::Ok(vi.clone());
}
Err(err) => {
@ -508,6 +521,7 @@ impl Context {
return Triple::Err(err);
}
}
_ => {}
}
}
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) {
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) => {
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) => {
if let Some(self_t) = vi.t.self_t() {
match self
@ -636,7 +652,7 @@ impl Context {
Triple::None
}
fn get_attr_from_nominal_t(
fn get_bound_attr_from_nominal_t(
&self,
obj: &hir::Expr,
ident: &Identifier,
@ -646,7 +662,7 @@ impl Context {
let self_t = obj.t();
if let Some(sups) = self.get_nominal_super_type_ctxs(&self_t) {
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) => {
return Triple::Ok(vi);
}
@ -657,7 +673,7 @@ impl Context {
}
// if self is a methods context
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) => {
return Triple::Ok(vi);
}
@ -691,7 +707,7 @@ impl Context {
}
};
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) => {
obj.ref_t().coerce();
return Triple::Ok(vi);
@ -702,7 +718,7 @@ impl Context {
_ => {}
}
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) => {
return Triple::Ok(vi);
}
@ -946,7 +962,7 @@ impl Context {
}
}
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) => {
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> {
match poly_spec.acc.to_string().trim_start_matches([':', '.']) {
"Array" => {
let ctx = self
.get_nominal_type_ctx(&array_t(Type::Obj, TyParam::Failure))
.unwrap()
.1;
// TODO: kw
let mut args = poly_spec.args.pos_args();
if let Some(first) = args.next() {
let t = self.instantiate_const_expr_as_type(
&first.expr,
None,
Some((ctx, 0)),
tmp_tv_cache,
not_found_is_qvar,
)?;
let len = if let Some(len) = args.next() {
self.instantiate_const_expr(
&len.expr,
None,
Some((ctx, 1)),
tmp_tv_cache,
not_found_is_qvar,
)?
@ -824,21 +828,24 @@ impl Context {
self.instantiate_acc(acc, erased_idx, tmp_tv_cache, not_found_is_qvar)
}
ast::ConstExpr::App(app) => {
let name = match &app.acc {
ast::ConstAccessor::Local(local) => local.inspect(),
_ => return type_feature_error!(self, app.loc(), "instantiating const callee"),
let ast::ConstAccessor::Local(ident) = &app.acc else {
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![];
for (i, arg) in app.args.pos_args().enumerate() {
let arg_t = self.instantiate_const_expr(
&arg.expr,
Some((self, i)),
Some((ctx, i)),
tmp_tv_cache,
not_found_is_qvar,
)?;
args.push(arg_t);
}
Ok(TyParam::app(name.clone(), args))
Ok(TyParam::app(ident.inspect().clone(), args))
}
ast::ConstExpr::Array(ConstArray::Normal(array)) => {
let mut tp_arr = vec![];

View file

@ -319,6 +319,10 @@ impl ContextKind {
matches!(self, Self::MethodDefs(_))
}
pub const fn is_trait_impl(&self) -> bool {
matches!(self, Self::MethodDefs(Some(_)))
}
pub const fn is_type(&self) -> bool {
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::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::erg_util::BUILTIN_ERG_MODS;
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::value::{GenTypeObj, TypeObj, ValueObj};
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;
@ -200,7 +201,10 @@ impl Context {
} else {
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(
self.cfg.input.clone(),
line!() as usize,
@ -266,7 +270,10 @@ impl Context {
self.absolutize(sig.ident.name.loc()),
);
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(
self.cfg.input.clone(),
line!() as usize,
@ -452,6 +459,7 @@ impl Context {
if self
.registered_info(name.inspect(), name.is_const())
.is_some()
&& &name.inspect()[..] != "_"
{
Err(TyCheckErrors::from(TyCheckError::reassign_error(
self.cfg.input.clone(),
@ -1287,9 +1295,7 @@ impl Context {
2,
self.level,
);
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?;
}
self.register_gen_mono_type(ident, gen, ctx, Const)
} else {
let params = gen
@ -1306,9 +1312,7 @@ impl Context {
2,
self.level,
);
if ERG_MODE {
self.gen_class_new_method(&gen, &mut ctx)?;
}
self.register_gen_poly_type(ident, gen, ctx, Const)
}
}
@ -1350,16 +1354,7 @@ impl Context {
..
} = additional
{
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(),
);
ctx.decls.insert(varname, vi);
}
self.register_instance_attrs(&mut ctx, rec)?;
}
param_t
.map(|t| self.intersection(t, additional.typ()))
@ -1421,16 +1416,7 @@ impl Context {
self.level,
);
let Some(TypeObj::Builtin{ t: Type::Record(req), .. }) = gen.base_or_sup() else { todo!("{gen}") };
for (field, t) in req.iter() {
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_instance_attrs(&mut ctx, req)?;
self.register_gen_mono_type(ident, gen, ctx, Const)
} else {
feature_error!(
@ -1463,16 +1449,7 @@ impl Context {
None
};
if let Some(additional) = additional {
for (field, t) in additional.iter() {
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_instance_attrs(&mut ctx, additional)?;
}
for sup in super_classes.into_iter() {
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<()> {
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() {
@ -1532,16 +1532,7 @@ impl Context {
t: Type::Record(rec),
..
} => {
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(),
);
ctx.decls.insert(varname, vi);
}
self.register_instance_attrs(ctx, rec)?;
}
other => {
methods.register_fixed_auto_impl(
@ -1557,6 +1548,7 @@ impl Context {
} else {
func0(gen.typ().clone())
};
if ERG_MODE {
methods.register_fixed_auto_impl(
"__new__",
new_t.clone(),
@ -1564,9 +1556,23 @@ impl Context {
Visibility::BUILTIN_PRIVATE,
Some("__call__".into()),
)?;
// 必要なら、ユーザーが独自に上書きする
// 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
.push((ClassDefType::Simple(gen.typ().clone()), methods));
Ok(())

View file

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

View file

@ -177,7 +177,7 @@ impl ASTLowerer {
for ctx in ctxs {
if let Triple::Ok(vi) = ctx.rec_get_var_info(
&ident.raw,
AccessKind::Attr,
AccessKind::UnboundAttr,
self.input(),
&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::io::Input;
use erg_common::switch_lang;
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::io::Input;
use erg_common::style::{StyledStr, StyledString, StyledStrings, Stylize};
use erg_common::traits::Locational;
use erg_common::{switch_lang, Str};
@ -485,11 +485,12 @@ impl LowerError {
) -> Self {
let hint = similar_name.map(|n| {
let vis = similar_info.map_or("".into(), |vi| vi.vis.modifier.display());
let kind = similar_info.map_or("", |vi| vi.kind.display());
switch_lang!(
"japanese" => format!("似た名前の{vis}属性があります: {n}"),
"simplified_chinese" => format!("具有相同名称的{vis}属性: {n}"),
"traditional_chinese" => format!("具有相同名稱的{vis}屬性: {n}"),
"english" => format!("has a similar name {vis} attribute: {n}"),
"japanese" => format!("似た名前の{vis}{kind}属性があります: {n}"),
"simplified_chinese" => format!("具有相同名称的{vis}{kind}属性: {n}"),
"traditional_chinese" => format!("具有相同名稱的{vis}{kind}屬性: {n}"),
"english" => format!("has a similar name {vis} {kind} attribute: {n}"),
)
});
let found = StyledString::new(name, Some(ERR), Some(ATTR));
@ -1157,4 +1158,30 @@ impl LowerWarning {
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 erg_common::config::Input;
use erg_common::error::{
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::traits::{Locational, Stream};
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::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 fmt::Display for CompileErrors {
@ -524,7 +536,7 @@ mod test {
ty::{Predicate, Type},
varinfo::{AbsLocation, VarInfo},
};
use erg_common::{config::Input, error::Location};
use erg_common::{error::Location, io::Input};
use erg_parser::ast::{VarName, VisModifierSpec};
// These Erg codes are not correct grammar.

View file

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

View file

@ -8,6 +8,9 @@
dtype: Type
size: Nat
.nan: Float
.Nan: Float
.abs: |T|(object: .NDArray(T),) -> .NDArray(T)
.add: |T|(object: .NDArray(T), other: .NDArray(T)) -> .NDArray(T)
.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))
};
match ASTBuilder::new(self.cfg().clone()).build(code) {
Ok(ast) => {
self.check_doc_ast(ast);
Ok(artifact) => {
self.check_doc_ast(artifact.ast);
}
Err(errs) => {
let errs = CompileErrors::from(errs);
self.errs.extend(errs);
Err(iart) => {
self.errs.extend(CompileErrors::from(iart.errors));
self.warns.extend(CompileErrors::from(iart.warns));
}
}
}

View file

@ -37,8 +37,8 @@ use crate::context::{
RegistrationMode, TraitImpl,
};
use crate::error::{
CompileError, CompileErrors, LowerError, LowerErrors, LowerResult, LowerWarning, LowerWarnings,
SingleLowerResult,
CompileError, CompileErrors, CompileWarning, LowerError, LowerErrors, LowerResult,
LowerWarning, LowerWarnings, SingleLowerResult,
};
use crate::hir;
use crate::hir::HIR;
@ -125,22 +125,27 @@ impl Runnable for ASTLowerer {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let mut ast_builder = ASTBuilder::new(self.cfg.copy());
let ast = ast_builder.build(self.cfg.input.read())?;
let artifact = self
.lower(ast, "exec")
let artifact = ast_builder
.build(self.cfg.input.read())
.map_err(|artifact| artifact.errors)?;
artifact.warns.fmt_all_stderr();
println!("{}", artifact.object);
artifact.warns.write_all_to(&mut self.cfg.output);
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()))
}
fn eval(&mut self, src: String) -> Result<String, Self::Errs> {
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
.lower(ast, "eval")
.lower(artifact.ast, "eval")
.map_err(|artifact| artifact.errors)?;
artifact.warns.fmt_all_stderr();
artifact.warns.write_all_stderr();
Ok(format!("{}", artifact.object))
}
}
@ -1663,6 +1668,23 @@ impl ASTLowerer {
self.pop_append_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(_) => {}
}
@ -1730,21 +1752,19 @@ impl ASTLowerer {
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);
}
let (__new__, need_to_gen_new) = if let (Some(dunder_new_vi), Some(new_vi)) = (
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 {
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 {
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));
Ok(hir::ClassDef::new(
type_obj.clone(),
hir_def.sig,
require_or_sup,
need_to_gen_new,
__new__,
__new__.t.clone(),
hir_methods,
))
}

View file

@ -1,5 +1,6 @@
use erg_common::config::ErgConfig;
use erg_common::error::MultiErrorDisplay;
use erg_common::io::Output;
use erg_common::spawn::exec_new_thread;
use erg_common::traits::Runnable;
@ -13,7 +14,8 @@ use erg_compiler::ty::constructors::{
use erg_compiler::ty::Type::*;
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);
lowerer.exec()?;
Ok(lowerer.pop_mod_ctx().unwrap())
@ -26,7 +28,7 @@ fn test_infer_types() -> Result<(), ()> {
fn _test_infer_types() -> Result<(), ()> {
let module = load_file("tests/infer.er").map_err(|errs| {
errs.fmt_all_stderr();
errs.write_all_stderr();
})?;
let t = type_q("T");
let u = type_q("U");

View file

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

View file

@ -444,6 +444,22 @@ impl SubrType {
|| 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> {
[
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 {
!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>(
&'a self,
min: Option<&'a TyParam>,

View file

@ -1062,6 +1062,29 @@ impl TyParam {
!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 {
match self {
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::sync::Arc;
use erg_common::config::Input;
use erg_common::dict::Dict;
use erg_common::error::{ErrorCore, ErrorKind, Location};
use erg_common::fresh::fresh_varname;
use erg_common::io::Input;
use erg_common::python_util::PythonVersion;
use erg_common::serialize::*;
use erg_common::set::Set;

View file

@ -3,7 +3,7 @@ use std::path::PathBuf;
use erg_common::error::Location;
use erg_common::set::Set;
use erg_common::Str;
use erg_common::{switch_lang, Str};
use erg_parser::ast::DefId;
@ -39,6 +39,7 @@ use Mutability::*;
pub enum VarKind {
Defined(DefId),
Declared,
InstanceAttr,
Parameter {
def_id: DefId,
var: bool,
@ -88,6 +89,38 @@ impl VarKind {
pub const fn is_builtin(&self) -> bool {
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)]
@ -285,12 +318,11 @@ impl VarInfo {
} else {
Mutability::Immutable
};
let kind = VarKind::Declared;
Self::new(
t,
muty,
Visibility::new(field.vis, namespace),
kind,
VarKind::InstanceAttr,
None,
impl_of,
None,

View file

@ -1004,6 +1004,15 @@ impl_nested_display_for_enum!(RecordAttrOrIdent; Attr, Ident);
impl_display_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)]
pub struct NormalSet {
pub l_brace: Token,

View file

@ -4,7 +4,7 @@ use erg_common::Str;
use crate::ast::AST;
use crate::desugar::Desugarer;
use crate::error::{ParserRunnerError, ParserRunnerErrors};
use crate::error::{CompleteArtifact, IncompleteArtifact, ParserRunnerError, ParserRunnerErrors};
use crate::parse::ParserRunner;
/// Summarize parsing and desugaring
@ -45,31 +45,55 @@ impl Runnable for ASTBuilder {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read();
let ast = self.build(src)?;
println!("{ast}");
let artifact = self.build(src).map_err(|iart| iart.errors)?;
println!("{}", artifact.ast);
Ok(ExitStatus::OK)
}
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.build(src)?;
Ok(format!("{ast}"))
let artifact = self.build(src).map_err(|iart| iart.errors)?;
Ok(format!("{}", artifact.ast))
}
}
impl ASTBuilder {
pub fn build(&mut self, src: String) -> Result<AST, ParserRunnerErrors> {
let module = self.runner.parse(src)?;
pub fn build(
&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 module = desugarer.desugar(module);
let name = self.runner.cfg().input.unescaped_filename();
let ast = AST::new(Str::rc(name), module);
Ok(ast)
let module = desugarer.desugar(artifact.ast);
let ast = AST::new(name, module);
Ok(CompleteArtifact::new(
ast,
ParserRunnerErrors::convert(self.input(), artifact.warns),
))
}
pub fn build_without_desugaring(&mut self, src: String) -> Result<AST, ParserRunnerErrors> {
let module = self.runner.parse(src)?;
let name = self.runner.cfg().input.unescaped_filename();
let ast = AST::new(Str::rc(name), module);
Ok(ast)
pub fn build_without_desugaring(
&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 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 erg_common::config::Input;
use erg_common::error::{
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::{fmt_iter, fmt_vec_split_with, impl_display_and_error, impl_stream, switch_lang};
use crate::ast::Module;
use crate::token::TokenKind;
#[derive(Debug)]
@ -50,6 +51,7 @@ impl fmt::Display for LexErrors {
impl std::error::Error for LexErrors {}
const ERR: Color = THEME.colors.error;
const WARN: Color = THEME.colors.warning;
const HINT: Color = THEME.colors.hint;
#[cfg(not(feature = "pretty"))]
const ATTR: Attribute = Attribute::Bold;
@ -534,12 +536,30 @@ impl LexError {
);
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 ParseError = LexError;
pub type ParseErrors = LexErrors;
pub type ParseWarning = LexError;
pub type ParseWarnings = LexErrors;
pub type ParseResult<T> = Result<T, ()>;
#[derive(Debug)]
@ -618,4 +638,71 @@ pub type ParserRunnerResult<T> = Result<T, ParserRunnerError>;
pub type LexerRunnerError = ParserRunnerError;
pub type LexerRunnerErrors = ParserRunnerErrors;
pub type ParserRunnerWarning = ParserRunnerError;
pub type ParserRunnerWarnings = ParserRunnerErrors;
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::config::ErgConfig;
use erg_common::config::Input;
use erg_common::io::Input;
use erg_common::traits::DequeStream;
use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{debug_power_assert, fn_name_full, normalize_newline, switch_lang};

View file

@ -5,8 +5,8 @@
use std::mem;
use erg_common::config::ErgConfig;
use erg_common::config::{Input, InputKind};
use erg_common::error::Location;
use erg_common::io::{Input, InputKind};
use erg_common::set::Set as HashSet;
use erg_common::str::Str;
use erg_common::traits::{DequeStream, ExitStatus, Locational, Runnable, Stream};
@ -17,7 +17,10 @@ use erg_common::{
use crate::ast::*;
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::token::{Token, TokenCategory, TokenKind, TokenStream};
@ -96,13 +99,13 @@ macro_rules! expect_pop {
}
pub trait Parsable {
fn parse(code: String) -> Result<Module, ParseErrors>;
fn parse(code: String) -> Result<CompleteArtifact, IncompleteArtifact<Module, ParseErrors>>;
}
pub struct 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 mut parser = Parser::new(ts);
parser.parse()
@ -416,59 +419,77 @@ impl Runnable for ParserRunner {
fn exec(&mut self) -> Result<ExitStatus, Self::Errs> {
let src = self.cfg_mut().input.read();
let ast = self.parse(src)?;
println!("{ast}");
let artifact = self.parse(src).map_err(|iart| iart.errors)?;
println!("{}", artifact.ast);
Ok(ExitStatus::OK)
}
fn eval(&mut self, src: String) -> Result<String, ParserRunnerErrors> {
let ast = self.parse(src)?;
Ok(format!("{ast}"))
let artifact = self.parse(src).map_err(|iart| iart.errors)?;
Ok(format!("{}", artifact.ast))
}
}
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)
.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()))
.lex()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?;
Parser::new(ts)
.parse()
.map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))
.map_err(|iart| iart.map_errs(|errs| ParserRunnerErrors::convert(self.input(), errs)))
}
}
impl Parser {
pub fn parse(&mut self) -> Result<Module, ParseErrors> {
pub fn parse(&mut self) -> Result<CompleteArtifact, IncompleteArtifact> {
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 "token stream: {}", self.tokens);
let module = match self.try_reduce_module() {
Ok(module) => module,
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) {
let loc = self.peek().map(|t| t.loc()).unwrap_or_default();
self.errs
.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 "AST:\n{module}");
if self.errs.is_empty() {
Ok(module)
Ok(CompleteArtifact::new(module, mem::take(&mut self.warns)))
} 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 {
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));
}
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::io::Input;
use erg_common::spawn::exec_new_thread;
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::ParserRunner;
#[test]
fn parse_args() -> Result<(), ()> {
expect_success("tests/args.er")
expect_success("tests/args.er", 0)
}
#[test]
fn parse_containers() -> Result<(), ()> {
expect_success("tests/containers.er")
expect_success("tests/containers.er", 0)
}
#[test]
fn parse_dependent() -> Result<(), ()> {
expect_success("tests/dependent.er")
expect_success("tests/dependent.er", 0)
}
#[test]
fn parse_fib() -> Result<(), ()> {
expect_success("tests/fib.er")
expect_success("tests/fib.er", 0)
}
#[test]
fn parse_hello_world() -> Result<(), ()> {
expect_success("tests/hello_world.er")
expect_success("tests/hello_world.er", 0)
}
#[test]
fn parse_simple_if() -> Result<(), ()> {
expect_success("tests/simple_if.er")
expect_success("tests/simple_if.er", 0)
}
#[test]
fn parse_stream() -> Result<(), ()> {
expect_success("tests/stream.er")
expect_success("tests/stream.er", 0)
}
#[test]
fn parse_test1_basic_syntax() -> Result<(), ()> {
expect_success("tests/test1_basic_syntax.er")
expect_success("tests/test1_basic_syntax.er", 0)
}
#[test]
fn parse_test2_advanced_syntax() -> Result<(), ()> {
expect_success("tests/test2_advanced_syntax.er")
expect_success("tests/test2_advanced_syntax.er", 0)
}
#[test]
fn parse_stack() -> Result<(), ()> {
expect_failure("tests/stack.er", 2)
expect_failure("tests/stack.er", 0, 2)
}
#[test]
fn parse_str_literal() -> Result<(), ()> {
expect_failure("tests/failed_str_lit.er", 2)
expect_failure("tests/failed_str_lit.er", 0, 2)
}
#[test]
fn parse_invalid_chunk() -> Result<(), ()> {
expect_failure("tests/invalid_chunk.er", 62)
expect_failure("tests/invalid_chunk.er", 0, 62)
}
#[test]
fn parse_invalid_collections() -> Result<(), ()> {
expect_failure("tests/invalid_collections.er", 29)
expect_failure("tests/invalid_collections.er", 0, 29)
}
#[test]
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 cfg = ErgConfig {
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 mut parser = ParserRunner::new(cfg);
match parser.parse_token_stream(
lexer
.lex()
.map_err(|errs| ParserRunnerErrors::convert(&input, errs))?,
) {
Ok(module) => {
println!("{module}");
Ok(())
match parser.parse_token_stream(lexer.lex().map_err(|errs| {
ErrorArtifact::new(
ParserRunnerErrors::empty(),
ParserRunnerErrors::convert(&input, errs),
)
})?) {
Ok(artifact) => {
if DEBUG_MODE {
println!("{}", artifact.ast);
}
Err(e) => {
e.fmt_all_stderr();
Err(e)
Ok(artifact.warns)
}
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)
}
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) {
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(()),
}
}
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) {
Ok(_) => Err(()),
Err(errs) => {
if errs.len() == errs_len {
Ok(())
} else {
println!("err: error length is not {errs_len} but {}", errs.len());
Err(eart) => match (eart.errors.len() == num_errs, eart.warns.len() == num_warns) {
(true, true) => Ok(()),
(true, false) => {
println!(
"err: number of warnings is not {num_warns} but {}",
eart.warns.len()
);
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 erg_common::config::Input;
use erg_common::io::Input;
// 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``.
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
foo = pyimport "foo"
assert foo.bar!(1) in Int
f: (Int -> Str) and (Str -> 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
decl_proc proc!: Proc, T =
x =>
assert x in T.Input
y = proc!(x)
assert y in T.Output
y
f(1): Str
f("1"): Int
```
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'>
<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のオブジェクトには可変性という概念があります。
以下のコードを例にします。

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のそれとは別物です。顕著な違いは、所有権システムと循環参照の禁止に現れています。

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の変数には __可視性__ という概念が存在します。
今まで見てきた変数は全て __プライベート変数(非公開変数)__ と呼ばれます。これは、外部から不可視の変数です。

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のサブルーチンには、外部変数を捕捉する「クロージャ」という機能があります。

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)。これをモジュールと呼びます。

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で使用可能なパターン

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)*}`でセット、

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型を使用します。
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との連携
[![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
@ -64,25 +64,45 @@ class C:
`d.er`内では宣言と定義(エイリアシング)以外の構文は使えません。
Pythonの関数はすべてプロシージャとして、クラスはすべて可変クラスとしてしか登録できないことに注意してください。
## オーバーロード
Pythonの型付けだけで使える特殊な型として、オーバーロード型があります。これは、複数の型を受け取ることができる型です。
```python
foo = pyimport "foo"
assert foo.bar!(1) in Int
f: (Int -> Str) and (Str -> Int)
```
これは、実行時に型チェックを行うことで型安全性を担保しています。チェック機構は概念的には以下のように動作します。
オーバーロード型はサブルーチン型のintersection(`and`)を取ることで宣言できます。`or`ではないことに注意してください。
こうすると、引数の型によって戻り値の型が変わる関数を宣言できます。
```python
decl_proc proc!: Proc, T =
x =>
assert x in T.Input
y = proc!(x)
assert y in T.Output
y
f(1): Str
f("1"): Int
```
これは実行時オーバーヘッドとなるので、[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'>
<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パッケージに大別できます。
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!`プロシージャを使う特殊なプロシージャです。

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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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) |

View file

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

View file

@ -1,8 +1,10 @@
#![allow(dead_code)]
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::io::{DummyStdin, Input, Output};
use erg_common::python_util::PythonVersion;
use erg_common::spawn::exec_new_thread;
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(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(())
}
}
@ -46,8 +50,10 @@ pub(crate) fn expect_success(file_path: &'static str, num_warns: usize) -> Resul
Err(())
}
Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(())
}
}
@ -71,8 +77,10 @@ pub(crate) fn expect_compile_success(file_path: &'static str, num_warns: usize)
Err(())
}
Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(())
}
}
@ -97,8 +105,10 @@ pub(crate) fn expect_repl_failure(
}
}
Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should succeed, but got compile errors");
errs.fmt_all_stderr();
Err(())
}
}
@ -119,8 +129,10 @@ pub(crate) fn expect_end_with(file_path: &'static str, code: i32) -> Result<(),
}
}
Err(errs) => {
if DEBUG_MODE {
errs.write_all_stderr();
}
println!("err: should end with {code}, but got compile errors");
errs.fmt_all_stderr();
Err(())
}
}
@ -148,7 +160,9 @@ pub(crate) fn expect_failure(
}
}
Err(errs) => {
errs.fmt_all_stderr();
if DEBUG_MODE {
errs.write_all_stderr();
}
if errs.len() == num_errs {
Ok(())
} else {
@ -184,7 +198,12 @@ fn set_cfg(mut cfg: ErgConfig) -> ErgConfig {
/// To execute on other versions, change the version and magic number.
fn _exec_file(file_path: &'static str) -> Result<ExitStatus, CompileErrors> {
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));
vm.exec()
}

View file

@ -10,7 +10,7 @@ use erg_compiler::Transpiler;
fn test_vm_embedding() -> Result<(), ()> {
let mut vm = DummyVM::default();
vm.eval("print! \"Hello, world!\"".into()).map_err(|es| {
es.fmt_all_stderr();
es.write_all_stderr();
})?;
vm.eval("prin \"Hello, world!\"".into())
.expect_err("should err");
@ -23,7 +23,7 @@ fn test_transpiler_embedding() -> Result<(), ()> {
let res = trans
.transpile("print!(\"\")".into(), "exec")
.map_err(|es| {
es.errors.fmt_all_stderr();
es.errors.write_all_stderr();
})?;
assert!(res.object.code.ends_with("(print)(Str(\"\"),)\n"));
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", "c"}, 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
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 10
h _: Int, _: Int = None
h 10, 20
#[
and: [Bool; 2] -> Bool
and [True, t] = t

View file

@ -273,6 +273,11 @@ fn exec_assert_cast() -> Result<(), ()> {
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]
fn exec_collection_err() -> Result<(), ()> {
expect_failure("tests/should_err/collection.er", 0, 4)
@ -345,7 +350,7 @@ fn exec_structural_err() -> Result<(), ()> {
#[test]
fn exec_subtyping_err() -> Result<(), ()> {
expect_failure("tests/should_err/subtyping.er", 0, 15)
expect_failure("tests/should_err/subtyping.er", 0, 17)
}
#[test]