mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-27 11:59:05 +00:00
Merge branch 'main' into pr/320
This commit is contained in:
commit
924b22a171
91 changed files with 1836 additions and 1027 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -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",
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
739
crates/erg_common/io.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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")
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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![];
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
) {
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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};
|
||||
|
|
0
crates/erg_compiler/lib/external/matplotlib.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/matplotlib.d/package.er
vendored
Normal 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
|
||||
|
|
0
crates/erg_compiler/lib/external/numpy.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/numpy.d/package.er
vendored
Normal file
5
crates/erg_compiler/lib/external/pandas.d/__init__.d.er
vendored
Normal file
5
crates/erg_compiler/lib/external/pandas.d/__init__.d.er
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
.DataFrame!;
|
||||
.Series!;
|
||||
.Index;
|
||||
} = pyimport "core/api"
|
0
crates/erg_compiler/lib/external/pandas.d/core.d/__init__.d.er
vendored
Normal file
0
crates/erg_compiler/lib/external/pandas.d/core.d/__init__.d.er
vendored
Normal file
3
crates/erg_compiler/lib/external/pandas.d/core.d/api.d.er
vendored
Normal file
3
crates/erg_compiler/lib/external/pandas.d/core.d/api.d.er
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{.DataFrame!;} = pyimport "./frame"
|
||||
{.Series!;} = pyimport "./series"
|
||||
{.Index;} = pyimport "./indexes/api"
|
14
crates/erg_compiler/lib/external/pandas.d/core.d/frame.d.er
vendored
Normal file
14
crates/erg_compiler/lib/external/pandas.d/core.d/frame.d.er
vendored
Normal 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
|
0
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/__init__.d.er
vendored
Normal file
0
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/__init__.d.er
vendored
Normal file
1
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/api.d.er
vendored
Normal file
1
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/api.d.er
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{.Index;} = pyimport "./base"
|
2
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/base.d.er
vendored
Normal file
2
crates/erg_compiler/lib/external/pandas.d/core.d/indexes.d/base.d.er
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
.Index: (T: Type) -> ClassType
|
||||
.Index(T) <: Output(T)
|
8
crates/erg_compiler/lib/external/pandas.d/core.d/series.d.er
vendored
Normal file
8
crates/erg_compiler/lib/external/pandas.d/core.d/series.d.er
vendored
Normal 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
|
0
crates/erg_compiler/lib/external/pandas.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/pandas.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/requests.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/requests.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/setuptools.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/setuptools.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/tqdm.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/tqdm.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/urllib3.d/package.er
vendored
Normal file
0
crates/erg_compiler/lib/external/urllib3.d/package.er
vendored
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::iter::Iterator;
|
||||
|
||||
use erg_common::config::Input;
|
||||
use erg_common::io::Input;
|
||||
|
||||
// use erg_compiler::parser;
|
||||
|
||||
|
|
1
crates/erg_parser/tests/warns.er
Normal file
1
crates/erg_parser/tests/warns.er
Normal file
|
@ -0,0 +1 @@
|
|||
_ = { .foo = 1; .foo = 1 } # WARN
|
|
@ -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>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# イテレータ
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
イテレータは、コンテナの要素を取り出すためのオブジェクトです。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 可変性
|
||||
|
||||
[](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)
|
||||
[](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のオブジェクトには可変性という概念があります。
|
||||
以下のコードを例にします。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 所有権システム
|
||||
|
||||
[](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)
|
||||
[](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のそれとは別物です。顕著な違いは、所有権システムと循環参照の禁止に現れています。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 可視性
|
||||
|
||||
[](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)
|
||||
[](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の変数には __可視性__ という概念が存在します。
|
||||
今まで見てきた変数は全て __プライベート変数(非公開変数)__ と呼ばれます。これは、外部から不可視の変数です。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 命名規則
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
変数を定数式として使いたい場合は、必ず大文字で始めます。二文字以降は小文字でもよいです。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 無名関数
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
無名関数は、関数オブジェクトを名付けずその場で生成するための文法です。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# サブルーチンシグネチャ
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
## 関数
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# クロージャ
|
||||
|
||||
[](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)
|
||||
[](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のサブルーチンには、外部変数を捕捉する「クロージャ」という機能があります。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# モジュール
|
||||
|
||||
[](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)
|
||||
[](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)。これをモジュールと呼びます。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# オブジェクトシステム
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
## オブジェクト(対象体)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# パターンマッチ、論駁可能性
|
||||
|
||||
[](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)
|
||||
[](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で使用可能なパターン
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 内包表記
|
||||
|
||||
[](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)
|
||||
[](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)*}`でセット、
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 展開代入
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
分解代入において、変数の前に`*`を置くと残りの要素を全てその変数に展開できます。これを展開代入と呼びます。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# デコレータ(修飾子)
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
デコレータは型や関数に特定の状態や振る舞いを追加したり明示するために使われます。
|
||||
デコレータの文法は以下の通りです。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# エラーハンドリング
|
||||
|
||||
[](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)
|
||||
[](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型オブジェクトを捨てる(トップレベルで対応しない)とエラーが発生します。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# パイプライン演算子
|
||||
|
||||
[](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)
|
||||
[](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)
|
||||
|
||||
パイプライン演算子は、次のように使います。
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Pythonとの連携
|
||||
|
||||
[](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)
|
||||
[](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>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# パッケージシステム
|
||||
|
||||
[](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)
|
||||
[](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`関数が実行されます。
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ジェネレータ
|
||||
|
||||
[](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)
|
||||
[](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!`プロシージャを使う特殊なプロシージャです。
|
||||
|
||||
|
|
|
@ -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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) [](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) |
|
||||
|
|
11
src/dummy.rs
11
src/dummy.rs
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
9
tests/should_err/class_attr.er
Normal file
9
tests/should_err/class_attr.er
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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}]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue