diff --git a/.cargo/config.toml b/.cargo/config.toml index 4da98a0b..19b46f57 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -12,6 +12,14 @@ cmp = "run -- --mode compile" ex = "run -- --mode exec" read = "run -- --mode read" +dlex = "run --features debug -- --mode lex" +dprs = "run --features debug -- --mode parse" +dlwr = "run --features debug -- --mode lower" +dchk = "run --features debug -- --mode check" +dcmp = "run --features debug -- --mode compile" +dex = "run --features debug -- --mode exec" +dread = "run --features debug -- --mode read" + rd = "run --features debug" rd_ja = "run --features debug --features japanese" rd_zh_cn = "run --features debug --features simplified_chinese" diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 1920302d..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**Reproducible code** -Or steps to reproduce the behavior: - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Result** - -**Screenshots (optional)** -If applicable, add screenshots to help explain your problem. - -**OS** - e.g. Windows 11 (WSL2) -If it is an obvious environment-independent bug (e.g. type inference bug), it is not necessary - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 00000000..2d210cad --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,58 @@ +name: 🐛 Bug report +description: Create a report to help us improve +labels: + - bug +body: + - type: textarea + attributes: + label: Describe the bug? + validations: + required: true + + - type: textarea + attributes: + label: Reproducible code + validations: + required: false + + - type: textarea + attributes: + label: Expected result + validations: + required: false + + - type: textarea + attributes: + label: Actual result + validations: + required: false + + - type: textarea + attributes: + label: Additional context + validations: + required: false + + - type: input + attributes: + label: Erg version + validations: + required: true + + - type: input + attributes: + label: Python version + validations: + required: false + + - type: dropdown + attributes: + label: os + options: + - Windows 10 + - Windows 11 + - MacOS 12 (Monterey) + - MacOS 11 (Big Sur) + - Ubuntu + - Linux(other distro) + - Other (write in `Additional context`) diff --git a/Cargo.lock b/Cargo.lock index b19cca6d..b5e0906e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,20 +2,9 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - [[package]] name = "erg" -version = "0.5.7" +version = "0.5.9-nightly.0" dependencies = [ "erg_common", "erg_compiler", @@ -25,14 +14,16 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.5.7" +version = "0.5.9-nightly.0" dependencies = [ - "atty", + "hermit-abi", + "libc", + "winapi", ] [[package]] name = "erg_compiler" -version = "0.5.7" +version = "0.5.9-nightly.0" dependencies = [ "erg_common", "erg_parser", @@ -41,14 +32,14 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.5.7" +version = "0.5.9-nightly.0" dependencies = [ "erg_common", ] [[package]] name = "erg_type" -version = "0.5.7" +version = "0.5.9-nightly.0" dependencies = [ "erg_common", "erg_parser", diff --git a/Cargo.toml b/Cargo.toml index fef1defd..78aeba1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "erg" -version = "0.5.7" +version = "0.5.9-nightly.0" description = "The Erg programming language" authors = ["erg-lang team "] license = "MIT OR Apache-2.0" @@ -18,6 +18,14 @@ members = [ "compiler/erg_type", ] +[workspace.package] +version = "0.5.9-nightly.0" +authors = ["erg-lang team "] +license = "MIT OR Apache-2.0" +edition = "2021" +repository = "https://github.com/erg-lang/erg" +homepage = "https://erg-lang.org/" + [features] # when "debug" feature is turned on, that of the following crates will also be turned on. debug = [ @@ -47,10 +55,10 @@ traditional_chinese = [ pre-commit = [] [dependencies] -erg_common = { version = "0.5.7", path = "./compiler/erg_common" } -erg_parser = { version = "0.5.7", path = "./compiler/erg_parser" } -erg_compiler = { version = "0.5.7", path = "./compiler/erg_compiler" } -erg_type = { version = "0.5.7", path = "./compiler/erg_type" } +erg_common = { version = "0.5.9-nightly.0", path = "./compiler/erg_common" } +erg_parser = { version = "0.5.9-nightly.0", path = "./compiler/erg_parser" } +erg_compiler = { version = "0.5.9-nightly.0", path = "./compiler/erg_compiler" } +erg_type = { version = "0.5.9-nightly.0", path = "./compiler/erg_type" } # [workspace] # member = ["cm", "dyne"] diff --git a/README.md b/README.md index 65187b44..6b5c548f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -
This is the main source code repository for [Erg](https://erg-lang.github.io/). This contains the compiler and documentation. +
This is the main source code repository for [Erg](https://erg-lang.org/). This contains the compiler and documentation.

Build status diff --git a/README_JA.md b/README_JA.md index c2d22a2f..3c2d71fc 100644 --- a/README_JA.md +++ b/README_JA.md @@ -4,7 +4,7 @@ -
こちらは[Erg](https://mtshiba.github.io/TheErgBook)のメインリポジトリです。コンパイラとドキュメントが置かれています。 +
こちらは[Erg](https://erg-lang.org/)のメインリポジトリです。コンパイラとドキュメントが置かれています。

Build status @@ -12,8 +12,8 @@

-[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3Df2118ff45d9e46ca8fa44242363223be43b046dd) -](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=f2118ff45d9e46ca8fa44242363223be43b046dd) +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3D1a66848fd63479093d0d7995712571e3fdc6bd92) +](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=1a66848fd63479093d0d7995712571e3fdc6bd92) ## Ergはこんな人におすすめです: diff --git a/README_zh-CN.md b/README_zh-CN.md index ec21a147..e63f01c5 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -4,7 +4,7 @@ -
这是[Erg](https://erg-lang.github.io/)的主要源代码库。它包含编译器和文档。 +
这是[Erg](https://erg-lang.org/)的主要源代码库。它包含编译器和文档。

Build status @@ -12,8 +12,8 @@

-[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3Df2118ff45d9e46ca8fa44242363223be43b046dd) -](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=f2118ff45d9e46ca8fa44242363223be43b046dd) +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3D1a66848fd63479093d0d7995712571e3fdc6bd92) +](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=1a66848fd63479093d0d7995712571e3fdc6bd92) ## Erg可以推荐给以下人员: diff --git a/README_zh-TW.md b/README_zh-TW.md index e51913ef..3286ffa0 100644 --- a/README_zh-TW.md +++ b/README_zh-TW.md @@ -4,7 +4,7 @@ -
這是[Erg](https://erg-lang.github.io/)的主要源代碼庫。它包含編譯器和文檔。 +
這是[Erg](https://erg-lang.org/)的主要源代碼庫。它包含編譯器和文檔。

Build status @@ -12,8 +12,8 @@

-[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3Df2118ff45d9e46ca8fa44242363223be43b046dd) -](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=f2118ff45d9e46ca8fa44242363223be43b046dd) +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3DREADME.md%26commit_hash%3D1a66848fd63479093d0d7995712571e3fdc6bd92) +](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=README.md&commit_hash=1a66848fd63479093d0d7995712571e3fdc6bd92) ## Erg可以推薦給以下人員: diff --git a/THIRD_PARTY_CREDITS.md b/THIRD_PARTY_CREDITS.md index 9301dbeb..402f422d 100644 --- a/THIRD_PARTY_CREDITS.md +++ b/THIRD_PARTY_CREDITS.md @@ -14,36 +14,35 @@ cargo license --json | \ ---- -## `atty` +## `crossterm` -* Source code is available at [https://github.com/softprops/atty](https://github.com/softprops/atty) +* Source code is available at [https://github.com/crossterm-rs/crossterm](https://github.com/crossterm-rs/crossterm) * license: MIT -### `atty`'s license text - -MIT from from [https://raw.githubusercontent.com/softprops/atty/0.2.14/LICENSE](https://raw.githubusercontent.com/softprops/atty/0.2.14/LICENSE): +### `crossterm`'s license text ```text -Copyright (c) 2015-2019 Doug Tangren +MIT License -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +Copyright (c) 2019 Timon -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. ``` ## `hermit-abi` diff --git a/compiler/erg_common/Cargo.toml b/compiler/erg_common/Cargo.toml index dcd057ee..96fc13f3 100644 --- a/compiler/erg_common/Cargo.toml +++ b/compiler/erg_common/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "erg_common" -version = "0.5.7" description = "A common components library of Erg" -authors = ["erg-lang team "] -license = "MIT OR Apache-2.0" -edition = "2021" -repository = "https://github.com/erg-lang/erg/tree/main/src/erg_common" -documentation = "https://docs.rs/erg_common" -homepage = "https://erg-lang.github.io/" +documentation = "http://docs.rs/erg_common" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -17,8 +17,13 @@ japanese = [] simplified_chinese = [] traditional_chinese = [] -[dependencies] -atty = "0.2.14" +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2", default-features = false } +[target.'cfg(target_os = "hermit")'.dependencies] +hermit-abi = "0.1.6" +[target.'cfg(windows)'.dependencies.winapi] +version = "0.3" +features = ["consoleapi"] [lib] path = "lib.rs" diff --git a/compiler/erg_common/config.rs b/compiler/erg_common/config.rs index a83a9869..abe6cd4c 100644 --- a/compiler/erg_common/config.rs +++ b/compiler/erg_common/config.rs @@ -211,48 +211,70 @@ impl ErgConfig { let mut cfg = Self::default(); // ループ内でnextするのでforにしないこと while let Some(arg) = args.next() { - let next_arg = args.next(); match &arg[..] { - "-c" if next_arg.is_some() => { - cfg.input = Input::Str(next_arg.unwrap()); + "-c" | "--code" => { + cfg.input = Input::Str(args.next().expect("the value of `-c` is not passed")); } "--dump-as-pyc" => { cfg.dump_as_pyc = true; } "-?" | "-h" | "--help" => { - // TODO: println!("{}", command_message()); + if let "--mode" = args.next().as_ref().map(|s| &s[..]).unwrap_or("") { + println!("{}", mode_message()); + } process::exit(0); } - "-m" if next_arg.is_some() => { - cfg.module = Box::leak(next_arg.unwrap().into_boxed_str()); + "-m" | "--module" => { + let module = args + .next() + .expect("the value of `-m` is not passed") + .into_boxed_str(); + cfg.module = Box::leak(module); } - "--mode" if next_arg.is_some() => { - let mode = next_arg.unwrap(); + "--mode" => { + let mode = args.next().expect("the value of `--mode` is not passed"); if let "-?" | "-h" | "--help" = &mode[..] { println!("{}", mode_message()); process::exit(0); } cfg.mode = Box::leak(mode.into_boxed_str()); } - "--ps1" if next_arg.is_some() => { - cfg.ps1 = Box::leak(next_arg.unwrap().into_boxed_str()); + "--ps1" => { + let ps1 = args + .next() + .expect("the value of `--ps1` is not passed") + .into_boxed_str(); + cfg.ps1 = Box::leak(ps1); } - "--ps2" if next_arg.is_some() => { - cfg.ps2 = Box::leak(next_arg.unwrap().into_boxed_str()); + "--ps2" => { + let ps2 = args + .next() + .expect("the value of `--ps2` is not passed") + .into_boxed_str(); + cfg.ps2 = Box::leak(ps2); } - "-o" | "--opt-level" | "--optimization-level" if next_arg.is_some() => { - cfg.opt_level = next_arg.unwrap().parse::().unwrap(); + "-o" | "--opt-level" | "--optimization-level" => { + cfg.opt_level = args + .next() + .expect("the value of `-o` is not passed") + .parse::() + .expect("the value of `-o` is not a number"); } - "-p" | "--py-ver" | "--python-version" if next_arg.is_some() => { - if let Ok(ver) = next_arg.unwrap().parse::() { - cfg.python_ver = Some(ver) - } + "-p" | "--py-ver" | "--python-version" => { + let py_ver = args + .next() + .expect("the value of `-p` is not passed") + .parse::() + .expect("the value of `-p` is not a number"); + cfg.python_ver = Some(py_ver); } - "--py-server-timeout" if next_arg.is_some() => { - if let Ok(time) = next_arg.unwrap().parse::() { - cfg.py_server_timeout = time; - } + "--py-server-timeout" => { + cfg.py_server_timeout = args + .next() + .expect("the value of `--py-server-timeout` is not passed") + .parse::() + .expect("the value of `--py-server-timeout` is not a number"); } "--quiet-startup" => { cfg.quiet_startup = true; @@ -260,10 +282,12 @@ impl ErgConfig { "-t" | "--show-type" => { cfg.show_type = true; } - "--verbose" if next_arg.is_some() => { - if let Ok(vr) = next_arg.unwrap().parse::() { - cfg.verbose = vr; - } + "--verbose" => { + cfg.verbose = args + .next() + .expect("the value of `--verbose` is not passed") + .parse::() + .expect("the value of `--verbose` is not a number"); } "-V" | "--version" => { println!("Erg {}", env!("CARGO_PKG_VERSION")); @@ -282,7 +306,8 @@ impl ErgConfig { } } if cfg.input == Input::REPL { - let is_stdin_piped = atty::isnt(atty::Stream::Stdin); + use crate::tty::IsTty; + let is_stdin_piped = !stdin().is_tty(); let input = if is_stdin_piped { let mut buffer = String::new(); stdin().read_to_string(&mut buffer).unwrap(); diff --git a/compiler/erg_common/help_messages.rs b/compiler/erg_common/help_messages.rs index 36c05a8d..cb3b1358 100644 --- a/compiler/erg_common/help_messages.rs +++ b/compiler/erg_common/help_messages.rs @@ -20,7 +20,7 @@ OPTIONS --python-version/-p (uint 32 number) Pythonバージョンを指定 --py-server-timeout (uint 64 number) PythonのREPLサーバーのタイムアウト時間を指定 --dump-as-pyc .pycファイルにダンプ - --mode lex|parse|compile|exec 指定モードで実行 + --mode lex|parse|compile|exec 指定モードで実行(詳細は--mode --helpを参照) SUBCOMMAND -c cmd : 文字列をプログラムに譲渡 @@ -43,7 +43,7 @@ OPTIONS --python-version/-p (uint 32 number) Python 版本 --py-server-timeout (uint 64 number) Python REPL 服务器超时 --dump-as-pyc 转储为 .pyc 文件 - --mode lex|parse|compile|exec 执行模式 + --mode lex|parse|compile|exec 执行模式 (更多信息见`--mode --help`) SUBCOMMAND -c cmd : 作为字符串传入程序 @@ -66,7 +66,7 @@ OPTIONS --python-version/-p (uint 32 number) Python 版本 --py-server-timeout (uint 64 number) Python REPL 服務器超時 --dump-as-pyc 轉儲為 .pyc 文件 - --mode lex|parse|compile|exec 執行模式 + --mode lex|parse|compile|exec 執行模式 (更多信息見`--mode --help`) SUBCOMMAND -c cmd : 作為字串傳入程式 @@ -89,7 +89,7 @@ OPTIONS --python-version/-p (uint 32 number) Python version --py-server-timeout (uint 64 number) timeout for the Python REPL server --dump-as-pyc dump as .pyc file - --mode lex|parse|compile|exec execution mode + --mode lex|parse|compile|exec execution mode (See `--mode --help` for details) SUBCOMMAND -c cmd : program passed in as string diff --git a/compiler/erg_common/lib.rs b/compiler/erg_common/lib.rs index 92fad5f5..8c27b7af 100644 --- a/compiler/erg_common/lib.rs +++ b/compiler/erg_common/lib.rs @@ -21,6 +21,7 @@ pub mod stdin; pub mod str; pub mod traits; pub mod tsort; +pub mod tty; pub mod vis; use crate::set::Set; diff --git a/compiler/erg_common/tty.rs b/compiler/erg_common/tty.rs new file mode 100644 index 00000000..70afa5b9 --- /dev/null +++ b/compiler/erg_common/tty.rs @@ -0,0 +1,63 @@ +//! Copied and modified from the [crossterm](https://github.com/crossterm-rs/crossterm). +//! Making it a little more convenient and safe to query whether +//! something is a terminal teletype or not. +//! This module defines the IsTty trait and the is_tty method to +//! return true if the item represents a terminal. + +#[cfg(unix)] +use std::os::unix::io::AsRawFd; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; + +#[cfg(windows)] +use winapi::um::consoleapi::GetConsoleMode; + +/// Adds the `is_tty` method to types that might represent a terminal +/// +/// ```rust +/// use std::io::stdout; +/// use erg_common::tty::IsTty; +/// +/// let is_tty: bool = stdout().is_tty(); +/// ``` +pub trait IsTty { + /// Returns true when an instance is a terminal teletype, otherwise false. + fn is_tty(&self) -> bool; +} + +/// On UNIX, the `isatty()` function returns true if a file +/// descriptor is a terminal. +#[cfg(unix)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let fd = self.as_raw_fd(); + unsafe { libc::isatty(fd) == 1 } + } +} + +#[cfg(target_os = "hermit")] +impl IsTty for S { + fn is_tty(&self) -> bool { + let fd = self.as_raw_fd(); + hermit_abi::isatty(fd) + } +} + +/// returns true if this is a tty +#[cfg(any(target_arch = "wasm32", target_env = "sgx"))] +impl IsTty for S { + fn is_tty(&self) -> bool { + false + } +} + +/// On windows, `GetConsoleMode` will return true if we are in a terminal. +/// Otherwise false. +#[cfg(windows)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let mut mode = 0; + let ok = unsafe { GetConsoleMode(self.as_raw_handle() as *mut _, &mut mode) }; + ok == 1 + } +} diff --git a/compiler/erg_compiler/Cargo.toml b/compiler/erg_compiler/Cargo.toml index 0b5c28fc..016697f0 100644 --- a/compiler/erg_compiler/Cargo.toml +++ b/compiler/erg_compiler/Cargo.toml @@ -1,25 +1,34 @@ [package] name = "erg_compiler" -version = "0.5.7" description = "Centimetre: the Erg compiler" -authors = ["erg-lang team "] -license = "MIT OR Apache-2.0" -edition = "2021" -repository = "https://github.com/erg-lang/erg/tree/main/src/compiler/erg_compiler" -documentation = "https://docs.rs/erg_compiler" -homepage = "https://erg-lang.github.io/" +documentation = "http://docs.rs/erg_compiler" +build = "build.rs" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true [features] # when "debug" feature is turned on, that of parser will also be turned on. -debug = [ "erg_common/debug", "erg_parser/debug", "erg_type/debug" ] -japanese = [ "erg_common/japanese", "erg_parser/japanese", "erg_type/japanese" ] -simplified_chinese = [ "erg_common/simplified_chinese", "erg_parser/simplified_chinese", "erg_type/simplified_chinese" ] -traditional_chinese = [ "erg_common/traditional_chinese", "erg_parser/traditional_chinese", "erg_type/traditional_chinese" ] +debug = ["erg_common/debug", "erg_parser/debug", "erg_type/debug"] +japanese = ["erg_common/japanese", "erg_parser/japanese", "erg_type/japanese"] +simplified_chinese = [ + "erg_common/simplified_chinese", + "erg_parser/simplified_chinese", + "erg_type/simplified_chinese", +] +traditional_chinese = [ + "erg_common/traditional_chinese", + "erg_parser/traditional_chinese", + "erg_type/traditional_chinese", +] [dependencies] -erg_common = { version = "0.5.7", path = "../erg_common" } -erg_parser = { version = "0.5.7", path = "../erg_parser" } -erg_type = { version = "0.5.7", path = "../erg_type" } +erg_common = { version = "0.5.9-nightly.0", path = "../erg_common" } +erg_parser = { version = "0.5.9-nightly.0", path = "../erg_parser" } +erg_type = { version = "0.5.9-nightly.0", path = "../erg_type" } [lib] path = "lib.rs" diff --git a/compiler/erg_compiler/build.rs b/compiler/erg_compiler/build.rs new file mode 100644 index 00000000..d9aecdd5 --- /dev/null +++ b/compiler/erg_compiler/build.rs @@ -0,0 +1,33 @@ +#![allow(deprecated)] + +use std::env; +use std::fs; +use std::path; + +fn main() -> std::io::Result<()> { + // Create a ".erg" directory + let erg_path = env::home_dir() + .expect("failed to get the location of the home dir") + .to_str() + .expect("invalid encoding of the home dir name") + .to_string() + + "/.erg"; + if !path::Path::new(&erg_path).exists() { + fs::create_dir(&erg_path)?; + fs::create_dir(format!("{erg_path}/std"))?; + } + println!("cargo:rustc-env=ERG_PATH={erg_path}"); + println!("cargo:rustc-env=ERG_STD_PATH={erg_path}/std"); + // create a std library in ".erg" + for res in fs::read_dir("std")? { + let entry = res?; + let path = entry.path(); + let filename = path + .file_name() + .expect("this is not a file") + .to_str() + .unwrap(); + fs::copy(&path, format!("{erg_path}/std/{filename}"))?; + } + Ok(()) +} diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 9eed940e..39d68594 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -29,7 +29,7 @@ use erg_type::value::ValueObj; use erg_type::{HasType, Type, TypeCode, TypePair}; use crate::compile::{AccessKind, Name, StoreLoadKind}; -use crate::context::eval::eval_lit; +use crate::context::eval::type_from_token_kind; use crate::error::CompileError; use crate::hir::{ Accessor, Args, Array, AttrDef, Attribute, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, @@ -867,7 +867,7 @@ impl CodeGenerator { self.write_instr(MAKE_FUNCTION); self.write_arg(0); self.emit_load_const(def.sig.ident().inspect().clone()); - self.emit_load_name_instr(Identifier::private(Str::ever("#ABCMeta"))); + self.emit_load_name_instr(Identifier::private("#ABCMeta")); self.emit_load_const(vec![ValueObj::from("metaclass")]); let subclasses_len = 1; self.write_instr(Opcode::CALL_FUNCTION_KW); @@ -917,7 +917,7 @@ impl CodeGenerator { self.emit_empty_func( Some(sig.ident().inspect()), def.sig.into_ident(), - Some(Identifier::private(Str::ever("#abstractmethod"))), + Some(Identifier::private("#abstractmethod")), ); } self.emit_load_const(ValueObj::None); @@ -1129,7 +1129,7 @@ impl CodeGenerator { CompileError::feature_error( self.cfg.input.clone(), unary.op.loc(), - "", + &unary.op.inspect().clone(), AtomicStr::from(unary.op.content), ) .write_to_stderr(); @@ -1150,6 +1150,9 @@ impl CodeGenerator { self.emit_load_name_instr(Identifier::public("range")); } TokenKind::LeftOpen | TokenKind::Closed | TokenKind::Open => todo!(), + TokenKind::InOp => { + self.emit_load_name_instr(Identifier::private("#in_operator")); + } _ => {} } let type_pair = TypePair::new(bin.lhs_t(), bin.rhs_t()); @@ -1170,14 +1173,16 @@ impl CodeGenerator { | TokenKind::NotEq | TokenKind::Gre | TokenKind::GreEq => COMPARE_OP, - TokenKind::LeftOpen | TokenKind::RightOpen | TokenKind::Closed | TokenKind::Open => { - CALL_FUNCTION - } // ERG_BINARY_RANGE, + TokenKind::LeftOpen + | TokenKind::RightOpen + | TokenKind::Closed + | TokenKind::Open + | TokenKind::InOp => CALL_FUNCTION, // ERG_BINARY_RANGE, _ => { CompileError::feature_error( self.cfg.input.clone(), bin.op.loc(), - "", + &bin.op.inspect().clone(), AtomicStr::from(bin.op.content), ) .write_to_stderr(); @@ -1191,14 +1196,22 @@ impl CodeGenerator { TokenKind::NotEq => 3, TokenKind::Gre => 4, TokenKind::GreEq => 5, - TokenKind::LeftOpen | TokenKind::RightOpen | TokenKind::Closed | TokenKind::Open => 2, + TokenKind::LeftOpen + | TokenKind::RightOpen + | TokenKind::Closed + | TokenKind::Open + | TokenKind::InOp => 2, _ => type_pair as u8, }; self.write_instr(instr); self.write_arg(arg); self.stack_dec(); match &bin.op.kind { - TokenKind::LeftOpen | TokenKind::RightOpen | TokenKind::Open | TokenKind::Closed => { + TokenKind::LeftOpen + | TokenKind::RightOpen + | TokenKind::Open + | TokenKind::Closed + | TokenKind::InOp => { self.stack_dec(); } _ => {} @@ -1339,7 +1352,11 @@ impl CodeGenerator { self.emit_store_instr(ident, AccessKind::Name); } ParamPattern::Lit(lit) => { - self.emit_load_const(eval_lit(&lit)); + let value = { + let t = type_from_token_kind(lit.token.kind); + ValueObj::from_str(t, lit.token.content).unwrap() + }; + self.emit_load_const(value); self.write_instr(Opcode::COMPARE_OP); self.write_arg(2); // == self.stack_dec(); @@ -1597,7 +1614,7 @@ impl CodeGenerator { log!(info "entered {} ({rec})", fn_name!()); let attrs_len = rec.attrs.len(); // making record type - let ident = Identifier::private(Str::ever("#NamedTuple")); + let ident = Identifier::private("#NamedTuple"); self.emit_load_name_instr(ident); // record name, let it be anonymous self.emit_load_const("Record"); @@ -1615,10 +1632,10 @@ impl CodeGenerator { self.write_arg(2); // (1 (subroutine) + argc + kwsc) input objects -> 1 return object self.stack_dec_n((1 + 2 + 0) - 1); - let ident = Identifier::private(Str::ever("#rec")); + let ident = Identifier::private("#rec"); self.emit_store_instr(ident, Name); // making record instance - let ident = Identifier::private(Str::ever("#rec")); + let ident = Identifier::private("#rec"); self.emit_load_name_instr(ident); for field in rec.attrs.into_iter() { self.emit_frameless_block(field.body.block, vec![]); @@ -1687,6 +1704,15 @@ impl CodeGenerator { self.stack_dec_n(len - 1); } } + Array::WithLength(arr) => { + self.emit_expr(*arr.elem); + self.write_instr(BUILD_LIST); + self.write_arg(1u8); + self.emit_expr(*arr.len); + self.write_instr(BINARY_MULTIPLY); + self.write_arg(0); + self.stack_dec(); + } other => todo!("{other}"), }, // TODO: tuple comprehension @@ -1720,7 +1746,11 @@ impl CodeGenerator { self.stack_dec_n(len - 1); } } - crate::hir::Set::WithLength(_) => todo!(), + crate::hir::Set::WithLength(st) => { + self.emit_expr(*st.elem); + self.write_instr(BUILD_SET); + self.write_arg(1u8); + } }, Expr::Record(rec) => self.emit_record(rec), Expr::Code(code) => { @@ -1739,7 +1769,7 @@ impl CodeGenerator { } // Dict, other => { - CompileError::feature_error(self.cfg.input.clone(), other.loc(), "???", "".into()) + CompileError::feature_error(self.cfg.input.clone(), other.loc(), "Dict", "".into()) .write_to_stderr(); self.crash("cannot compile this expression at this time"); } @@ -1894,7 +1924,7 @@ impl CodeGenerator { attrs.push(Expr::AttrDef(attr_def)); } let none = Token::new(TokenKind::NoneLit, "None", line, 0); - attrs.push(Expr::Lit(Literal::from(none))); + attrs.push(Expr::Lit(Literal::try_from(none).unwrap())); } other => todo!("{other}"), } @@ -2007,15 +2037,40 @@ impl CodeGenerator { fn load_prelude(&mut self) { self.load_record_type(); + self.load_prelude_py(); self.record_type_loaded = true; } + fn load_prelude_py(&mut self) { + self.emit_global_import_items( + Identifier::public("sys"), + vec![( + Identifier::public("path"), + Some(Identifier::private("#path")), + )], + ); + self.emit_load_name_instr(Identifier::private("#path")); + self.emit_load_method_instr("Array!", None, Identifier::public("push!")); + self.emit_load_const(env!("ERG_STD_PATH")); + self.write_instr(CALL_METHOD); + self.write_arg(1u8); + self.stack_dec(); + self.emit_pop_top(); + self.emit_global_import_items( + Identifier::public("_erg_std_prelude"), + vec![( + Identifier::public("in_operator"), + Some(Identifier::private("#in_operator")), + )], + ); + } + fn load_record_type(&mut self) { self.emit_global_import_items( Identifier::public("collections"), vec![( Identifier::public("namedtuple"), - Some(Identifier::private(Str::ever("#NamedTuple"))), + Some(Identifier::private("#NamedTuple")), )], ); } @@ -2026,11 +2081,11 @@ impl CodeGenerator { vec![ ( Identifier::public("ABCMeta"), - Some(Identifier::private(Str::ever("#ABCMeta"))), + Some(Identifier::private("#ABCMeta")), ), ( Identifier::public("abstractmethod"), - Some(Identifier::private(Str::ever("#abstractmethod"))), + Some(Identifier::private("#abstractmethod")), ), ], ); @@ -2041,7 +2096,7 @@ impl CodeGenerator { Identifier::public("types"), vec![( Identifier::public("ModuleType"), - Some(Identifier::private(Str::ever("#ModuleType"))), + Some(Identifier::private("#ModuleType")), )], ); } diff --git a/compiler/erg_compiler/context/compare.rs b/compiler/erg_compiler/context/compare.rs index 16489a4c..a5aca769 100644 --- a/compiler/erg_compiler/context/compare.rs +++ b/compiler/erg_compiler/context/compare.rs @@ -681,25 +681,26 @@ impl Context { pub(crate) fn cyclic_supertype_of(&self, lhs: &FreeTyVar, rhs: &Type) -> bool { let subst_ctx = SubstContext::new(rhs, self, Location::Unknown); if let Some(super_traits) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_traits) { - for sup_trait in super_traits { - let sup_trait = if sup_trait.has_qvar() { - subst_ctx.substitute(sup_trait.clone()).unwrap() + for super_trait in super_traits { + let sup_trait = if super_trait.has_qvar() { + subst_ctx.substitute(super_trait.clone()).unwrap() } else { - sup_trait.clone() + super_trait.clone() }; if self.sup_conforms(lhs, rhs, &sup_trait) { return true; } } } - if let Some(sup_classes) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_classes) { - for sup_class in sup_classes { - let sup_class = if sup_class.has_qvar() { - subst_ctx.substitute(sup_class.clone()).unwrap() + if let Some(super_classes) = self.get_super_classes(rhs) { + for super_class in super_classes { + let sup_class = if super_class.has_qvar() { + subst_ctx.substitute(super_class).unwrap() } else { - sup_class.clone() + super_class }; if self.cyclic_supertype_of(lhs, &sup_class) { + log!(err "引っかかった: {lhs}, {sup_class}"); return true; } } @@ -849,6 +850,9 @@ impl Context { /// returns union of two types (A or B) pub(crate) fn union(&self, lhs: &Type, rhs: &Type) -> Type { + if lhs == rhs { + return lhs.clone(); + } // `?T or ?U` will not be unified // `Set!(?T, 3) or Set(?T, 3)` wii be unified to Set(?T, 3) if !lhs.is_unbound_var() && !rhs.is_unbound_var() { @@ -888,6 +892,9 @@ impl Context { /// returns intersection of two types (A and B) pub(crate) fn intersection(&self, lhs: &Type, rhs: &Type) -> Type { + if lhs == rhs { + return lhs.clone(); + } // ?T and ?U will not be unified if !lhs.is_unbound_var() && !rhs.is_unbound_var() { match (self.supertype_of(lhs, rhs), self.subtype_of(lhs, rhs)) { diff --git a/compiler/erg_compiler/context/eval.rs b/compiler/erg_compiler/context/eval.rs index 2a74bd58..76680742 100644 --- a/compiler/erg_compiler/context/eval.rs +++ b/compiler/erg_compiler/context/eval.rs @@ -100,12 +100,6 @@ fn op_to_name(op: OpKind) -> &'static str { } } -#[inline] -pub(crate) fn eval_lit(lit: &Literal) -> ValueObj { - let t = type_from_token_kind(lit.token.kind); - ValueObj::from_str(t, lit.token.content.clone()) -} - /// Instantiate the polymorphic type from the quantified state. /// /// e.g. @@ -577,13 +571,26 @@ impl Context { Ok(ValueObj::Subr(subr)) } + pub(crate) fn eval_lit(&self, lit: &Literal) -> EvalResult { + let t = type_from_token_kind(lit.token.kind); + ValueObj::from_str(t, lit.token.content.clone()).ok_or_else(|| { + EvalError::invalid_literal( + self.cfg.input.clone(), + line!() as usize, + lit.token.loc(), + self.caused_by(), + ) + .into() + }) + } + pub(crate) fn eval_const_expr( &self, expr: &Expr, __name__: Option<&Str>, ) -> EvalResult { match expr { - Expr::Lit(lit) => Ok(eval_lit(lit)), + Expr::Lit(lit) => self.eval_lit(lit), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::UnaryOp(unary) => self.eval_const_unary(unary), @@ -603,7 +610,7 @@ impl Context { __name__: Option<&Str>, ) -> EvalResult { match expr { - Expr::Lit(lit) => Ok(eval_lit(lit)), + Expr::Lit(lit) => self.eval_lit(lit), Expr::Accessor(acc) => self.eval_const_acc(acc), Expr::BinOp(bin) => self.eval_const_bin(bin), Expr::UnaryOp(unary) => self.eval_const_unary(unary), diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index 3a7d01e5..ebfd0a88 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -109,9 +109,14 @@ impl Context { panic!("{} has already been registered as const", t.name()); } else { let name = VarName::from_str(t.name()); + let meta_t = match ctx.kind { + ContextKind::Class => Type::ClassType, + ContextKind::Trait => Type::TraitType, + _ => Type::Type, + }; self.locals.insert( name.clone(), - VarInfo::new(Type, muty, Private, Builtin, None), + VarInfo::new(meta_t, muty, Private, Builtin, None), ); self.consts .insert(name.clone(), ValueObj::builtin_t(t.clone())); @@ -156,9 +161,14 @@ impl Context { root_ctx.methods_list.push((ClassDefType::Simple(t), ctx)); } else { let name = VarName::from_str(t.name()); + let meta_t = match ctx.kind { + ContextKind::Class => Type::ClassType, + ContextKind::Trait => Type::TraitType, + _ => Type::Type, + }; self.locals.insert( name.clone(), - VarInfo::new(Type, muty, Private, Builtin, None), + VarInfo::new(meta_t, muty, Private, Builtin, None), ); self.consts .insert(name.clone(), ValueObj::builtin_t(t.clone())); @@ -203,7 +213,7 @@ impl Context { let name = VarName::from_static(name); self.locals.insert( name.clone(), - VarInfo::new(Type, muty, Private, Builtin, None), + VarInfo::new(Patch, muty, Private, Builtin, None), ); for method_name in ctx.locals.keys() { if let Some(patches) = self.method_impl_patches.get_mut(method_name) { @@ -454,7 +464,7 @@ impl Context { /* Obj */ let mut obj = Self::builtin_mono_class("Obj", 2); let t = fn0_met(mono_q("Self"), mono_q("Self")); - let t = quant(t, set! {subtypeof(mono_q("Self"), builtin_mono("Obj"))}); + let t = quant(t, set! {subtypeof(mono_q("Self"), Obj)}); obj.register_builtin_impl("clone", t, Const, Public); obj.register_builtin_impl("__module__", Str, Const, Public); obj.register_builtin_impl("__sizeof__", fn0_met(Obj, Nat), Const, Public); @@ -469,10 +479,11 @@ impl Context { ); let mut obj_in = Self::builtin_methods("In", 2); obj_in.register_builtin_impl("__in__", fn1_met(Obj, Type, Bool), Const, Public); - obj.register_trait(Obj, builtin_poly("Eq", vec![ty_tp(Type)]), obj_in); + obj.register_trait(Obj, builtin_poly("In", vec![ty_tp(Type)]), obj_in); let mut obj_mutizable = Self::builtin_methods("Mutizable", 1); obj_mutizable.register_builtin_const("MutType!", ValueObj::builtin_t(builtin_mono("Obj!"))); obj.register_trait(Obj, builtin_mono("Mutizable"), obj_mutizable); + // Obj does not implement Eq /* Float */ let mut float = Self::builtin_mono_class("Float", 2); @@ -760,6 +771,24 @@ impl Context { let mut str_show = Self::builtin_methods("Show", 1); str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public); str_.register_trait(Str, builtin_mono("Show"), str_show); + /* NoneType */ + let mut nonetype = Self::builtin_mono_class("NoneType", 10); + nonetype.register_superclass(Obj, &obj); + let mut nonetype_eq = Self::builtin_methods("Eq", 2); + nonetype_eq.register_builtin_impl( + "__eq__", + fn1_met(NoneType, NoneType, Bool), + Const, + Public, + ); + nonetype.register_trait( + NoneType, + builtin_poly("Eq", vec![ty_tp(NoneType)]), + nonetype_eq, + ); + let mut nonetype_show = Self::builtin_methods("Show", 1); + nonetype_show.register_builtin_impl("to_str", fn0_met(NoneType, Str), Immutable, Public); + nonetype.register_trait(NoneType, builtin_mono("Show"), nonetype_show); /* Type */ let mut type_ = Self::builtin_mono_class("Type", 2); type_.register_superclass(Obj, &obj); @@ -783,6 +812,21 @@ impl Context { builtin_poly("Eq", vec![ty_tp(ClassType)]), class_eq, ); + let mut trait_type = Self::builtin_mono_class("TraitType", 2); + trait_type.register_superclass(Type, &type_); + trait_type.register_marker_trait(builtin_mono("Named")); + let mut trait_eq = Self::builtin_methods("Eq", 2); + trait_eq.register_builtin_impl( + "__eq__", + fn1_met(TraitType, TraitType, Bool), + Const, + Public, + ); + trait_type.register_trait( + TraitType, + builtin_poly("Eq", vec![ty_tp(TraitType)]), + trait_eq, + ); let g_module_t = builtin_mono("GenericModule"); let mut generic_module = Self::builtin_mono_class("GenericModule", 2); generic_module.register_superclass(Obj, &obj); @@ -849,7 +893,15 @@ impl Context { Immutable, Public, ); - array_.register_trait(array_t, builtin_mono("Show"), array_show); + array_.register_trait(array_t.clone(), builtin_mono("Show"), array_show); + let array_type_t = builtin_poly("ArrayType", vec![mono_q_tp("T"), mono_q_tp("N")]); + let mut array_type = Self::builtin_poly_class( + "ArrayType", + vec![PS::named_nd("T", Type), PS::named_nd("N", Nat)], + 2, + ); + array_type.register_superclass(array_t.clone(), &array_); + array_type.register_superclass(Type, &type_); /* Set */ let mut set_ = Self::builtin_poly_class("Set", vec![PS::t_nd("T"), PS::named_nd("N", Nat)], 10); @@ -892,6 +944,14 @@ impl Context { let mut set_show = Self::builtin_methods("Show", 1); set_show.register_builtin_impl("to_str", fn0_met(set_t.clone(), Str), Immutable, Public); set_.register_trait(set_t.clone(), builtin_mono("Show"), set_show); + let set_type_t = builtin_poly("SetType", vec![mono_q_tp("T"), mono_q_tp("N")]); + let mut set_type = Self::builtin_poly_class( + "SetType", + vec![PS::named_nd("T", Type), PS::named_nd("N", Nat)], + 2, + ); + set_type.register_superclass(set_t.clone(), &set_); + set_type.register_superclass(Type, &type_); /* Bytes */ let mut bytes = Self::builtin_mono_class("Bytes", 2); bytes.register_superclass(Obj, &obj); @@ -1117,243 +1177,16 @@ impl Context { ), tuple5_eq, ); - let mut tuple6 = Self::builtin_poly_class( - "Tuple6", - vec![ - PS::t_nd("A"), - PS::t_nd("B"), - PS::t_nd("C"), - PS::t_nd("D"), - PS::t_nd("E"), - PS::t_nd("F"), - ], - 2, - ); - tuple6.register_superclass(builtin_mono("Tuple"), &tuple_); - let mut tuple6_eq = Self::builtin_methods("Eq", 2); - tuple6_eq.register_builtin_impl( - "__eq__", - fn1_met( - builtin_poly( - "Tuple6", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ], - ), - builtin_poly( - "Tuple6", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ], - ), - Bool, - ), - Const, - Public, - ); - tuple6.register_trait( - builtin_poly( - "Tuple6", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ], - ), - builtin_poly( - "Eq", - vec![ty_tp(builtin_poly( - "Tuple6", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ], - ))], - ), - tuple6_eq, - ); - let mut tuple7 = Self::builtin_poly_class( - "Tuple7", - vec![ - PS::t_nd("A"), - PS::t_nd("B"), - PS::t_nd("C"), - PS::t_nd("D"), - PS::t_nd("E"), - PS::t_nd("F"), - PS::t_nd("G"), - ], - 2, - ); - tuple7.register_superclass(builtin_mono("Tuple"), &tuple_); - let mut tuple7_eq = Self::builtin_methods("Eq", 2); - tuple7_eq.register_builtin_impl( - "__eq__", - fn1_met( - builtin_poly( - "Tuple7", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ], - ), - builtin_poly( - "Tuple7", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ], - ), - Bool, - ), - Const, - Public, - ); - tuple7.register_trait( - builtin_poly( - "Tuple7", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ], - ), - builtin_poly( - "Eq", - vec![ty_tp(builtin_poly( - "Tuple7", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ], - ))], - ), - tuple7_eq, - ); - let mut tuple8 = Self::builtin_poly_class( - "Tuple8", - vec![ - PS::t_nd("A"), - PS::t_nd("B"), - PS::t_nd("C"), - PS::t_nd("D"), - PS::t_nd("E"), - PS::t_nd("F"), - PS::t_nd("G"), - PS::t_nd("H"), - ], - 2, - ); - tuple8.register_superclass(builtin_mono("Tuple"), &tuple_); - let mut tuple8_eq = Self::builtin_methods("Eq", 2); - tuple8_eq.register_builtin_impl( - "__eq__", - fn1_met( - builtin_poly( - "Tuple8", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ty_tp(mono_q("H")), - ], - ), - builtin_poly( - "Tuple8", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ty_tp(mono_q("H")), - ], - ), - Bool, - ), - Const, - Public, - ); - tuple8.register_trait( - builtin_poly( - "Tuple8", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ty_tp(mono_q("H")), - ], - ), - builtin_poly( - "Eq", - vec![ty_tp(builtin_poly( - "Tuple8", - vec![ - ty_tp(mono_q("A")), - ty_tp(mono_q("B")), - ty_tp(mono_q("C")), - ty_tp(mono_q("D")), - ty_tp(mono_q("E")), - ty_tp(mono_q("F")), - ty_tp(mono_q("G")), - ty_tp(mono_q("H")), - ], - ))], - ), - tuple8_eq, - ); /* record */ let mut record = Self::builtin_mono_class("Record", 2); record.register_superclass(Obj, &obj); let mut record_type = Self::builtin_mono_class("RecordType", 2); record_type.register_superclass(builtin_mono("Record"), &record); - record_type.register_superclass(builtin_mono("Type"), &type_); + record_type.register_superclass(Type, &type_); + /* Or (true or type) */ + let or_t = builtin_poly("Or", vec![ty_tp(mono_q("L")), ty_tp(mono_q("R"))]); + let mut or = Self::builtin_poly_class("Or", vec![PS::t_nd("L"), PS::t_nd("R")], 2); + or.register_superclass(Obj, &obj); /* Float_mut */ let mut float_mut = Self::builtin_mono_class("Float!", 2); float_mut.register_superclass(Float, &float); @@ -1378,15 +1211,7 @@ impl Context { ratio_mut.register_superclass(Ratio, &ratio); let mut ratio_mut_mutable = Self::builtin_methods("Mutable", 2); ratio_mut_mutable.register_builtin_const("ImmutType", ValueObj::builtin_t(Ratio)); - let f_t = kw( - "f", - func( - vec![kw("old", builtin_mono("Ratio"))], - None, - vec![], - builtin_mono("Ratio"), - ), - ); + let f_t = kw("f", func(vec![kw("old", Ratio)], None, vec![], Ratio)); let t = pr_met( ref_mut(builtin_mono("Ratio!"), None), vec![f_t], @@ -1462,7 +1287,7 @@ impl Context { ); /* Str_mut */ let mut str_mut = Self::builtin_mono_class("Str!", 2); - str_mut.register_superclass(Str, &str_); + str_mut.register_superclass(Str, &nonetype); let mut str_mut_mutable = Self::builtin_methods("Mutable", 2); str_mut_mutable.register_builtin_const("ImmutType", ValueObj::builtin_t(Str)); let f_t = kw("f", func(vec![kw("old", Str)], None, vec![], Str)); @@ -1500,7 +1325,6 @@ impl Context { file_mut_readable, ); /* Array_mut */ - let array_t = builtin_poly("Array", vec![ty_tp(mono_q("T")), mono_q_tp("N")]); let array_mut_t = builtin_poly("Array!", vec![ty_tp(mono_q("T")), mono_q_tp("N")]); let mut array_mut_ = Self::builtin_poly_class( "Array!", @@ -1617,7 +1441,8 @@ impl Context { /* Range */ let range_t = builtin_poly("Range", vec![TyParam::t(mono_q("T"))]); let mut range = Self::builtin_poly_class("Range", vec![PS::t_nd("T")], 2); - range.register_superclass(Obj, &obj); + // range.register_superclass(Obj, &obj); + range.register_superclass(Type, &type_); range.register_marker_trait(builtin_poly("Output", vec![ty_tp(mono_q("T"))])); let mut range_eq = Self::builtin_methods("Eq", 2); range_eq.register_builtin_impl( @@ -1653,12 +1478,16 @@ impl Context { self.register_builtin_type(Ratio, ratio, Const); self.register_builtin_type(Bool, bool_, Const); self.register_builtin_type(Str, str_, Const); + self.register_builtin_type(NoneType, nonetype, Const); self.register_builtin_type(Type, type_, Const); self.register_builtin_type(ClassType, class_type, Const); + self.register_builtin_type(TraitType, trait_type, Const); self.register_builtin_type(g_module_t, generic_module, Const); self.register_builtin_type(module_t, module, Const); self.register_builtin_type(array_t, array_, Const); + self.register_builtin_type(array_type_t, array_type, Const); self.register_builtin_type(set_t, set_, Const); + self.register_builtin_type(set_type_t, set_type, Const); self.register_builtin_type(builtin_mono("Bytes"), bytes, Const); self.register_builtin_type(tuple(vec![mono_q("A")]), tuple1, Const); self.register_builtin_type(tuple(vec![mono_q("A"), mono_q("B")]), tuple2, Const); @@ -1683,47 +1512,9 @@ impl Context { tuple5, Const, ); - self.register_builtin_type( - tuple(vec![ - mono_q("A"), - mono_q("B"), - mono_q("C"), - mono_q("D"), - mono_q("E"), - mono_q("F"), - ]), - tuple6, - Const, - ); - self.register_builtin_type( - tuple(vec![ - mono_q("A"), - mono_q("B"), - mono_q("C"), - mono_q("D"), - mono_q("E"), - mono_q("F"), - mono_q("G"), - ]), - tuple7, - Const, - ); - self.register_builtin_type( - tuple(vec![ - mono_q("A"), - mono_q("B"), - mono_q("C"), - mono_q("D"), - mono_q("E"), - mono_q("F"), - mono_q("G"), - mono_q("H"), - ]), - tuple8, - Const, - ); self.register_builtin_type(builtin_mono("Record"), record, Const); self.register_builtin_type(builtin_mono("RecordType"), record_type, Const); + self.register_builtin_type(or_t, or, Const); self.register_builtin_type(builtin_mono("Int!"), int_mut, Const); self.register_builtin_type(builtin_mono("Nat!"), nat_mut, Const); self.register_builtin_type(builtin_mono("Float!"), float_mut, Const); @@ -1744,12 +1535,19 @@ impl Context { fn init_builtin_funcs(&mut self) { let t_abs = nd_func(vec![kw("n", builtin_mono("Num"))], None, Nat); + let t_ascii = nd_func(vec![kw("object", Obj)], None, Str); let t_assert = func( vec![kw("condition", Bool)], None, vec![kw("err_message", Str)], NoneType, ); + let t_bin = nd_func(vec![kw("n", Int)], None, Str); + let t_chr = nd_func( + vec![kw("i", Type::from(value(0usize)..=value(1_114_111usize)))], + None, + Str, + ); let t_classof = nd_func(vec![kw("old", Obj)], None, ClassType); let t_compile = nd_func(vec![kw("src", Str)], None, Code); let t_cond = nd_func( @@ -1786,6 +1584,27 @@ impl Context { module(mono_q_tp("Path")), ); let t_import = quant(t_import, set! {static_instance("Path", Str)}); + let t_isinstance = nd_func( + vec![ + kw("object", Obj), + kw("classinfo", ClassType), // TODO: => ClassInfo + ], + None, + Bool, + ); + let t_issubclass = nd_func( + vec![ + kw("subclass", ClassType), + kw("classinfo", ClassType), // TODO: => ClassInfo + ], + None, + Bool, + ); + let t_len = nd_func( + vec![kw("s", builtin_poly("Seq", vec![TyParam::erased(Type)]))], + None, + Nat, + ); let t_log = func( vec![], Some(kw("objects", ref_(Obj))), @@ -1797,31 +1616,57 @@ impl Context { ], NoneType, ); + let t_oct = nd_func(vec![kw("x", Int)], None, Str); + let t_ord = nd_func(vec![kw("c", Str)], None, Nat); + let t_panic = nd_func(vec![kw("err_message", Str)], None, Never); + let m = mono_q("M"); + // TODO: mod + let t_pow = nd_func( + vec![kw("base", m.clone()), kw("exp", m.clone())], + None, + m.clone(), + ); + let t_pow = quant( + t_pow, + set! {static_instance("M", builtin_poly("Mul", vec![ty_tp(m)]))}, + ); let t_pyimport = nd_func( vec![anon(tp_enum(Str, set! {mono_q_tp("Path")}))], None, module(mono_q_tp("Path")), ); - let t_panic = nd_func(vec![kw("err_message", Str)], None, NoneType); let t_pyimport = quant(t_pyimport, set! {static_instance("Path", Str)}); let t_quit = func(vec![], None, vec![kw("code", Int)], NoneType); let t_exit = t_quit.clone(); + let t_repr = nd_func(vec![kw("object", Obj)], None, Str); + let t_round = nd_func(vec![kw("number", Float)], None, Int); self.register_builtin_impl("abs", t_abs, Immutable, Private); + self.register_builtin_impl("ascii", t_ascii, Immutable, Private); self.register_builtin_impl("assert", t_assert, Const, Private); // assert casting に悪影響が出る可能性があるため、Constとしておく + self.register_builtin_impl("bin", t_bin, Immutable, Private); + self.register_builtin_impl("chr", t_chr, Immutable, Private); self.register_builtin_impl("classof", t_classof, Immutable, Private); self.register_builtin_impl("compile", t_compile, Immutable, Private); self.register_builtin_impl("cond", t_cond, Immutable, Private); self.register_builtin_impl("discard", t_discard, Immutable, Private); self.register_builtin_impl("exit", t_exit, Immutable, Private); self.register_builtin_impl("if", t_if, Immutable, Private); - self.register_builtin_impl("log", t_log, Immutable, Private); self.register_builtin_impl("import", t_import, Immutable, Private); + self.register_builtin_impl("isinstance", t_isinstance, Immutable, Private); + self.register_builtin_impl("issubclass", t_issubclass, Immutable, Private); + self.register_builtin_impl("len", t_len, Immutable, Private); + self.register_builtin_impl("log", t_log, Immutable, Private); + self.register_builtin_impl("oct", t_oct, Immutable, Private); + self.register_builtin_impl("ord", t_ord, Immutable, Private); self.register_builtin_impl("panic", t_panic, Immutable, Private); + self.register_builtin_impl("pow", t_pow, Immutable, Private); if cfg!(feature = "debug") { self.register_builtin_impl("py", t_pyimport.clone(), Immutable, Private); } self.register_builtin_impl("pyimport", t_pyimport, Immutable, Private); self.register_builtin_impl("quit", t_quit, Immutable, Private); + self.register_builtin_impl("repr", t_repr, Immutable, Private); + self.register_builtin_impl("round", t_round, Immutable, Private); } fn init_builtin_const_funcs(&mut self) { @@ -2051,7 +1896,7 @@ impl Context { self.register_builtin_decl("__rorng__", op_t.clone(), Private); self.register_builtin_decl("__orng__", op_t, Private); // TODO: use existential type: |T: Type| (T, In(T)) -> Bool - let op_t = bin_op(mono_q("T"), mono_q("I"), Bool); + let op_t = bin_op(mono_q("I"), mono_q("T"), Bool); let op_t = quant( op_t, set! { static_instance("T", Type), subtypeof(mono_q("I"), builtin_poly("In", vec![ty_tp(mono_q("T"))])) }, diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 435ed970..46f9bd7e 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -17,7 +17,7 @@ use erg_parser::ast::{self, Identifier}; use erg_parser::token::Token; use erg_type::constructors::{ - anon, builtin_mono, free_var, func, module, mono_proj, subr_t, v_enum, + anon, builtin_mono, builtin_poly, free_var, func, module, mono_proj, subr_t, v_enum, }; use erg_type::free::Constraint; use erg_type::typaram::TyParam; @@ -87,6 +87,31 @@ impl Context { }) } + pub(crate) fn get_mut_current_scope_var(&mut self, name: &str) -> Option<&mut VarInfo> { + self.locals + .get_mut(name) + .or_else(|| self.decls.get_mut(name)) + .or_else(|| { + self.params + .iter_mut() + .find(|(opt_name, _)| { + opt_name + .as_ref() + .map(|n| &n.inspect()[..] == name) + .unwrap_or(false) + }) + .map(|(_, vi)| vi) + }) + .or_else(|| { + for (_, methods) in self.methods_list.iter_mut() { + if let Some(vi) = methods.get_mut_current_scope_var(name) { + return Some(vi); + } + } + None + }) + } + pub(crate) fn get_local_kv(&self, name: &str) -> Option<(&VarName, &VarInfo)> { self.locals.get_key_value(name) } @@ -1160,8 +1185,19 @@ impl Context { } pub(crate) fn get_similar_name(&self, name: &str) -> Option<&str> { + match name { + "true" => return Some("True"), + "false" => return Some("False"), + "Null" | "Nil" | "null" | "nil" | "none" => return Some("None"), + "del" => return Some("Del"), + "int" => return Some("Int"), + "nat" => return Some("Nat"), + "str" => return Some("Str"), + "bool" => return Some("Bool"), + _ => {} + } let name = readable_name(name); - // TODO: add decls + // REVIEW: add decls? get_similar_name( self.params .iter() @@ -1298,35 +1334,6 @@ impl Context { concatenated } - pub(crate) fn _get_nominal_super_trait_ctxs<'a>( - &'a self, - t: &Type, - ) -> Option> { - let ctx = self.get_nominal_type_ctx(t)?; - Some(ctx.super_traits.iter().map(|sup| { - let sup_ctx = self - .get_nominal_type_ctx(sup) - .unwrap_or_else(|| todo!("{} not found", sup)); - sup_ctx - })) - } - - pub(crate) fn _get_nominal_super_class_ctxs<'a>( - &'a self, - t: &Type, - ) -> Option> { - // if `t` is {S: Str | ...}, `ctx_t` will be Str - // else if `t` is Array(Int, 10), `ctx_t` will be Array(T, N) (if Array(Int, 10) is not specialized) - let ctx = self.get_nominal_type_ctx(t)?; - // t: {S: Str | ...} => ctx.super_traits: [Eq(Str), Mul(Nat), ...] - // => return: [(Str, Eq(Str)), (Str, Mul(Nat)), ...] (the content of &'a Type isn't {S: Str | ...}) - Some( - ctx.super_classes - .iter() - .map(|sup| self.get_nominal_type_ctx(sup).unwrap()), - ) - } - pub(crate) fn get_nominal_super_type_ctxs<'a>(&'a self, t: &Type) -> Option> { match t { Type::FreeVar(fv) if fv.is_linked() => self.get_nominal_super_type_ctxs(&fv.crack()), @@ -1364,6 +1371,7 @@ impl Context { } } + /// include `t` itself fn get_simple_nominal_super_type_ctxs<'a>( &'a self, t: &Type, @@ -1377,6 +1385,19 @@ impl Context { Some(vec![ctx].into_iter().chain(sups)) } + /// if `typ` is a refinement type, include the base type (refine.t) + pub(crate) fn get_super_classes(&self, typ: &Type) -> Option> { + self.get_nominal_type_ctx(typ).map(|ctx| { + let super_classes = ctx.super_classes.clone(); + let derefined = typ.derefine(); + if typ != &derefined { + vec![derefined].into_iter().chain(super_classes) + } else { + vec![].into_iter().chain(super_classes) + } + }) + } + // TODO: Never pub(crate) fn get_nominal_type_ctx<'a>(&'a self, typ: &Type) -> Option<&'a Context> { match typ { @@ -1433,7 +1454,7 @@ impl Context { } Type::Poly { path, name, .. } => { if self.path() == path { - if let Some((_, ctx)) = self.rec_get_mono_type(name) { + if let Some((_, ctx)) = self.rec_get_poly_type(name) { return Some(ctx); } } @@ -1448,7 +1469,7 @@ impl Context { .and_then(|cache| cache.ref_ctx(path.as_path())) }) { - if let Some((_, ctx)) = ctx.rec_get_mono_type(name) { + if let Some((_, ctx)) = ctx.rec_get_poly_type(name) { return Some(ctx); } } @@ -1495,16 +1516,10 @@ impl Context { return Some(res); } } - Type::Or(l, r) => { - let lctx = self.get_nominal_type_ctx(l)?; - let rctx = self.get_nominal_type_ctx(r)?; - // use smaller context - return match (self.supertype_of(l, r), self.supertype_of(r, l)) { - (true, true) => Some(lctx), - (true, false) => Some(rctx), - (false, true) => Some(lctx), - (false, false) => None, - }; + Type::Or(_l, _r) => { + if let Some(ctx) = self.get_nominal_type_ctx(&builtin_poly("Or", vec![])) { + return Some(ctx); + } } // FIXME: `F()`などの場合、実際は引数が省略されていてもmonomorphicになる other if other.is_monomorphic() => { diff --git a/compiler/erg_compiler/context/instantiate.rs b/compiler/erg_compiler/context/instantiate.rs index 549dc1ef..b0e41032 100644 --- a/compiler/erg_compiler/context/instantiate.rs +++ b/compiler/erg_compiler/context/instantiate.rs @@ -26,7 +26,6 @@ use erg_type::{HasType, ParamTy, Predicate, SubrKind, TyBound, Type}; use TyParamOrdering::*; use Type::*; -use crate::context::eval::eval_lit; use crate::context::{Context, RegistrationMode}; use crate::error::{SingleTyCheckResult, TyCheckError, TyCheckErrors, TyCheckResult}; use crate::hir; @@ -577,7 +576,7 @@ impl Context { self.instantiate_typespec(&spec_with_op.t_spec, opt_decl_t, tmp_tv_ctx, mode)? } else { match &sig.pat { - ast::ParamPattern::Lit(lit) => v_enum(set![eval_lit(lit)]), + ast::ParamPattern::Lit(lit) => v_enum(set![self.eval_lit(lit)?]), // TODO: Array _ => { let level = if mode == PreRegister { @@ -613,7 +612,7 @@ impl Context { let t = self.instantiate_param_sig_t(sig, opt_decl_t, tmp_tv_ctx, mode)?; match (sig.inspect(), &sig.opt_default_val) { (Some(name), Some(default)) => { - let default = self.instantiate_const_expr(default); + let default = self.instantiate_const_expr(default)?; Ok(ParamTy::kw_default( name.clone(), t, @@ -665,7 +664,7 @@ impl Context { if let Some(first) = args.next() { let t = self.instantiate_const_expr_as_type(&first.expr)?; let len = args.next().unwrap(); - let len = self.instantiate_const_expr(&len.expr); + let len = self.instantiate_const_expr(&len.expr)?; Ok(array(t, len)) } else { Ok(builtin_mono("GenericArray")) @@ -707,23 +706,28 @@ impl Context { } other => { // FIXME: kw args - let params = simple.args.pos_args().map(|arg| match &arg.expr { - ast::ConstExpr::Lit(lit) => TyParam::Value(eval_lit(lit)), - _ => { - todo!() + let mut new_params = vec![]; + for arg in simple.args.pos_args() { + match &arg.expr { + ast::ConstExpr::Lit(lit) => { + new_params.push(TyParam::Value(self.eval_lit(lit)?)); + } + _ => { + todo!() + } } - }); + } // FIXME: non-builtin - Ok(builtin_poly(Str::rc(other), params.collect())) + Ok(builtin_poly(Str::rc(other), new_params)) } } } - pub(crate) fn instantiate_const_expr(&self, expr: &ast::ConstExpr) -> TyParam { + pub(crate) fn instantiate_const_expr(&self, expr: &ast::ConstExpr) -> TyCheckResult { match expr { - ast::ConstExpr::Lit(lit) => TyParam::Value(eval_lit(lit)), + ast::ConstExpr::Lit(lit) => Ok(TyParam::Value(self.eval_lit(lit)?)), ast::ConstExpr::Accessor(ast::ConstAccessor::Local(name)) => { - TyParam::Mono(name.inspect().clone()) + Ok(TyParam::Mono(name.inspect().clone())) } _ => todo!(), } @@ -780,13 +784,13 @@ impl Context { )), TypeSpec::Array(arr) => { let elem_t = self.instantiate_typespec(&arr.ty, opt_decl_t, tmp_tv_ctx, mode)?; - let len = self.instantiate_const_expr(&arr.len); - Ok(array(elem_t, len)) + let len = self.instantiate_const_expr(&arr.len)?; + Ok(builtin_poly("ArrayType", vec![ty_tp(elem_t), len])) } TypeSpec::Set(set) => { let elem_t = self.instantiate_typespec(&set.ty, opt_decl_t, tmp_tv_ctx, mode)?; - let len = self.instantiate_const_expr(&set.len); - Ok(erg_type::constructors::set(elem_t, len)) + let len = self.instantiate_const_expr(&set.len)?; + Ok(builtin_poly("SetType", vec![ty_tp(elem_t), len])) } // FIXME: unwrap TypeSpec::Tuple(tys) => Ok(tuple( @@ -797,18 +801,18 @@ impl Context { }) .collect(), )), - // TODO: エラー処理(リテラルでない、ダブりがある)はパーサーにやらせる - TypeSpec::Enum(set) => Ok(v_enum( - set.pos_args() - .map(|arg| { - if let ast::ConstExpr::Lit(lit) = &arg.expr { - eval_lit(lit) - } else { - todo!() - } - }) - .collect::>(), - )), + // TODO: エラー処理(リテラルでない)はパーサーにやらせる + TypeSpec::Enum(set) => { + let mut new_set = set! {}; + for arg in set.pos_args() { + if let ast::ConstExpr::Lit(lit) = &arg.expr { + new_set.insert(self.eval_lit(lit)?); + } else { + todo!() + } + } + Ok(v_enum(new_set)) + } TypeSpec::Interval { op, lhs, rhs } => { let op = match op.kind { TokenKind::Closed => IntervalOp::Closed, @@ -817,9 +821,9 @@ impl Context { TokenKind::Open => IntervalOp::Open, _ => assume_unreachable!(), }; - let l = self.instantiate_const_expr(lhs); + let l = self.instantiate_const_expr(lhs)?; let l = self.eval_tp(&l)?; - let r = self.instantiate_const_expr(rhs); + let r = self.instantiate_const_expr(rhs)?; let r = self.eval_tp(&r)?; if let Some(Greater) = self.try_cmp(&l, &r) { panic!("{l}..{r} is not a valid interval type (should be lhs <= rhs)") diff --git a/compiler/erg_compiler/context/mod.rs b/compiler/erg_compiler/context/mod.rs index d77fcd6b..39451fd5 100644 --- a/compiler/erg_compiler/context/mod.rs +++ b/compiler/erg_compiler/context/mod.rs @@ -281,6 +281,7 @@ pub enum OperationKind { Import, PyImport, Del, + AssertCast, } impl OperationKind { diff --git a/compiler/erg_compiler/context/register.rs b/compiler/erg_compiler/context/register.rs index 20793b79..5067af8e 100644 --- a/compiler/erg_compiler/context/register.rs +++ b/compiler/erg_compiler/context/register.rs @@ -14,8 +14,9 @@ use ast::{DefId, Identifier, VarName}; use erg_parser::ast; use erg_type::constructors::{func, func1, proc, ref_, ref_mut, v_enum}; +use erg_type::free::{Constraint, Cyclicity, FreeKind}; use erg_type::value::{GenTypeObj, TypeKind, TypeObj, ValueObj}; -use erg_type::{ParamTy, SubrType, Type}; +use erg_type::{HasType, ParamTy, SubrType, Type}; use crate::build_hir::HIRBuilder; use crate::context::{ @@ -1076,4 +1077,75 @@ impl Context { ))) } } + + pub(crate) fn cast( + &mut self, + type_spec: ast::TypeSpec, + call: &mut hir::Call, + ) -> TyCheckResult<()> { + let cast_to = + self.instantiate_typespec(&type_spec, None, None, RegistrationMode::Normal)?; + let lhs = enum_unwrap!( + call.args.get_mut_left_or_key("pred").unwrap(), + hir::Expr::BinOp + ) + .lhs + .as_mut(); + match ( + self.supertype_of(lhs.ref_t(), &cast_to), + self.subtype_of(lhs.ref_t(), &cast_to), + ) { + // assert 1 in {1} + (true, true) => Ok(()), + // assert x in Int (x: Nat) + (false, true) => Ok(()), // TODO: warn (needless) + // assert x in Nat (x: Int) + (true, false) => { + if let hir::Expr::Accessor(ref acc) = lhs { + self.change_var_type(acc, cast_to.clone())?; + } + match lhs.ref_t() { + Type::FreeVar(fv) if fv.is_linked() => { + let constraint = Constraint::new_subtype_of(cast_to, Cyclicity::Not); + fv.replace(FreeKind::new_unbound(self.level, constraint)); + } + Type::FreeVar(fv) => { + let new_constraint = Constraint::new_subtype_of(cast_to, Cyclicity::Not); + fv.update_constraint(new_constraint); + } + _ => { + *lhs.ref_mut_t() = cast_to; + } + } + Ok(()) + } + // assert x in Str (x: Int) + (false, false) => Err(TyCheckErrors::from(TyCheckError::invalid_type_cast_error( + self.cfg.input.clone(), + line!() as usize, + lhs.loc(), + self.caused_by(), + &lhs.to_string(), + &cast_to, + None, + ))), + } + } + + fn change_var_type(&mut self, acc: &hir::Accessor, t: Type) -> TyCheckResult<()> { + #[allow(clippy::single_match)] + match acc { + hir::Accessor::Ident(ident) => { + if let Some(vi) = self.get_mut_current_scope_var(ident.inspect()) { + vi.t = t; + } else { + todo!() + } + } + _ => { + // TODO: support other accessors + } + } + Ok(()) + } } diff --git a/compiler/erg_compiler/context/tyvar.rs b/compiler/erg_compiler/context/tyvar.rs index 1f71eedc..ce6c202b 100644 --- a/compiler/erg_compiler/context/tyvar.rs +++ b/compiler/erg_compiler/context/tyvar.rs @@ -671,6 +671,13 @@ impl Context { } Ok(()) } + hir::Array::WithLength(arr) => { + let loc = arr.loc(); + arr.t = self.deref_tyvar(mem::take(&mut arr.t), Covariant, loc)?; + self.resolve_expr_t(&mut arr.elem)?; + self.resolve_expr_t(&mut arr.len)?; + Ok(()) + } _ => todo!(), }, hir::Expr::Tuple(tuple) => match tuple { @@ -683,12 +690,20 @@ impl Context { }, hir::Expr::Set(set) => match set { hir::Set::Normal(st) => { + let loc = st.loc(); + st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, loc)?; for elem in st.elems.pos_args.iter_mut() { self.resolve_expr_t(&mut elem.expr)?; } Ok(()) } - hir::Set::WithLength(_) => todo!(), + hir::Set::WithLength(st) => { + let loc = st.loc(); + st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, loc)?; + self.resolve_expr_t(&mut st.elem)?; + self.resolve_expr_t(&mut st.len)?; + Ok(()) + } }, hir::Expr::Dict(_dict) => { todo!() diff --git a/compiler/erg_compiler/error.rs b/compiler/erg_compiler/error.rs index 023449ad..efbb743d 100644 --- a/compiler/erg_compiler/error.rs +++ b/compiler/erg_compiler/error.rs @@ -990,6 +990,30 @@ impl EvalError { caused_by, ) } + + pub fn invalid_literal( + input: Input, + errno: usize, + loc: Location, + caused_by: AtomicStr, + ) -> Self { + Self::new( + ErrorCore::new( + errno, + SyntaxError, + loc, + switch_lang!( + "japanese" => "リテラルが不正です", + "simplified_chinese" => "字面量不合法", + "traditional_chinese" => "字面量不合法", + "english" => "invalid literal", + ), + None, + ), + input, + caused_by, + ) + } } pub type EffectError = TyCheckError; @@ -1568,6 +1592,34 @@ impl LowerError { caused_by, ) } + + #[allow(clippy::too_many_arguments)] + pub fn invalid_type_cast_error( + input: Input, + errno: usize, + loc: Location, + caused_by: AtomicStr, + name: &str, + cast_to: &Type, + hint: Option, + ) -> Self { + Self::new( + ErrorCore::new( + errno, + TypeError, + loc, + switch_lang!( + "japanese" => format!("{YELLOW}{name}{RESET}の型を{RED}{cast_to}{RESET}にキャストすることはできません"), + "simplified_chinese" => format!("{YELLOW}{name}{RESET}的类型无法转换为{RED}{cast_to}{RESET}"), + "traditional_chinese" => format!("{YELLOW}{name}{RESET}的類型無法轉換為{RED}{cast_to}{RESET}"), + "english" => format!("the type of {YELLOW}{name}{RESET} cannot be cast to {RED}{cast_to}{RESET}"), + ), + hint, + ), + input, + caused_by, + ) + } } #[derive(Debug)] diff --git a/compiler/erg_compiler/hir.rs b/compiler/erg_compiler/hir.rs index 3e376277..d83c85fe 100644 --- a/compiler/erg_compiler/hir.rs +++ b/compiler/erg_compiler/hir.rs @@ -47,14 +47,16 @@ impl Locational for Literal { } } -impl From for Literal { - fn from(token: Token) -> Self { - let data = ValueObj::from_str(type_from_token_kind(token.kind), token.content.clone()); - Self { +impl TryFrom for Literal { + type Error = (); + fn try_from(token: Token) -> Result { + let data = + ValueObj::from_str(type_from_token_kind(token.kind), token.content.clone()).ok_or(())?; + Ok(Self { t: data.t(), value: data, token, - } + }) } } @@ -282,6 +284,20 @@ impl Args { } } + pub fn get_mut_left_or_key(&mut self, key: &str) -> Option<&mut Expr> { + if !self.pos_args.is_empty() { + Some(&mut self.pos_args.get_mut(0)?.expr) + } else if let Some(pos) = self + .kw_args + .iter() + .position(|arg| &arg.keyword.inspect()[..] == key) + { + Some(&mut self.kw_args.get_mut(pos)?.expr) + } else { + None + } + } + pub fn insert_pos(&mut self, idx: usize, pos: PosArg) { self.pos_args.insert(idx, pos); } @@ -351,8 +367,8 @@ impl Identifier { ) } - pub fn private(name: Str) -> Self { - Self::bare(None, VarName::from_str(name)) + pub fn private(name: &'static str) -> Self { + Self::bare(None, VarName::from_static(name)) } pub fn private_with_line(name: Str, line: usize) -> Self { diff --git a/compiler/erg_compiler/link.rs b/compiler/erg_compiler/link.rs index 98ac3ed3..1d2ad423 100644 --- a/compiler/erg_compiler/link.rs +++ b/compiler/erg_compiler/link.rs @@ -59,6 +59,10 @@ impl<'a> Linker<'a> { self.replace_import(&mut elem.expr); } } + Array::WithLength(arr) => { + self.replace_import(&mut arr.elem); + self.replace_import(&mut arr.len); + } _ => todo!(), }, Expr::Tuple(tuple) => match tuple { @@ -74,9 +78,11 @@ impl<'a> Linker<'a> { self.replace_import(&mut elem.expr); } } - Set::WithLength(_) => todo!(), + Set::WithLength(st) => { + self.replace_import(&mut st.elem); + self.replace_import(&mut st.len); + } }, - Expr::Dict(_dict) => { todo!() } @@ -241,7 +247,7 @@ impl<'a> Linker<'a> { mod_name_lit.ln_begin().unwrap(), mod_name_lit.col_begin().unwrap(), ); - let mod_name = Expr::Lit(Literal::from(token)); + let mod_name = Expr::Lit(Literal::try_from(token).unwrap()); args.insert_pos(0, PosArg::new(mod_name)); let line = expr.ln_begin().unwrap_or(0); for attr in comps { diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 832c80fe..0ca580cf 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -15,6 +15,7 @@ use erg_parser::ast; use erg_parser::ast::AST; use erg_parser::build_ast::ASTBuilder; use erg_parser::token::{Token, TokenKind}; +use erg_parser::Parser; use erg_type::constructors::{ array, array_mut, builtin_mono, builtin_poly, free_var, func, mono, proc, quant, set, set_mut, @@ -182,6 +183,19 @@ impl ASTLowerer { } } + fn lower_literal(&self, lit: ast::Literal) -> LowerResult { + let loc = lit.loc(); + let lit = hir::Literal::try_from(lit.token).map_err(|_| { + LowerError::invalid_literal( + self.cfg.input.clone(), + line!() as usize, + loc, + self.ctx.caused_by(), + ) + })?; + Ok(lit) + } + fn lower_array(&mut self, array: ast::Array) -> LowerResult { log!(info "entered {}({array})", fn_name!()); match array { @@ -260,6 +274,8 @@ impl ASTLowerer { "ArrayWithMutType!", vec![TyParam::t(elem.t()), TyParam::Value(v)], ) + } else if self.ctx.subtype_of(&elem.t(), &Type::Type) { + builtin_poly("ArrayType", vec![TyParam::t(elem.t()), TyParam::Value(v)]) } else { array(elem.t(), TyParam::Value(v)) } @@ -429,6 +445,8 @@ impl ASTLowerer { "SetWithMutType!", vec![TyParam::t(elem.t()), TyParam::Value(v)], ) + } else if self.ctx.subtype_of(&elem.t(), &Type::Type) { + builtin_poly("SetType", vec![TyParam::t(elem.t()), TyParam::Value(v)]) } else { set(elem.t(), TyParam::Value(v)) } @@ -489,7 +507,7 @@ impl ASTLowerer { } ast::Accessor::TupleAttr(t_attr) => { let obj = self.lower_expr(*t_attr.obj)?; - let index = hir::Literal::from(t_attr.index.token); + let index = self.lower_literal(t_attr.index)?; let n = enum_unwrap!(index.value, ValueObj::Nat); let t = enum_unwrap!( obj.ref_t().typarams().get(n as usize).unwrap().clone(), @@ -562,9 +580,27 @@ impl ASTLowerer { Ok(hir::UnaryOp::new(unary.op, expr, t)) } - // TODO: single `import` fn lower_call(&mut self, call: ast::Call) -> LowerResult { log!(info "entered {}({}{}(...))", fn_name!(), call.obj, fmt_option!(call.method_name)); + let opt_cast_to = if call.is_assert_cast() { + if let Some(typ) = call.assert_cast_target_type() { + Some(Parser::expr_to_type_spec(typ.clone()).map_err(|e| { + let e = LowerError::new(e.into(), self.input().clone(), self.ctx.caused_by()); + LowerErrors::from(e) + })?) + } else { + return Err(LowerErrors::from(LowerError::syntax_error( + self.input().clone(), + line!() as usize, + call.args.loc(), + self.ctx.caused_by(), + "invalid assert casting type", + None, + ))); + } + } else { + None + }; let (pos_args, kw_args, paren) = call.args.deconstruct(); let mut hir_args = hir::Args::new( Vec::with_capacity(pos_args.len()), @@ -597,7 +633,7 @@ impl ASTLowerer { } else { None }; - let call = hir::Call::new(obj, method_name, hir_args, sig_t); + let mut call = hir::Call::new(obj, method_name, hir_args, sig_t); match call.additional_operation() { Some(kind @ (OperationKind::Import | OperationKind::PyImport)) => { let mod_name = @@ -621,7 +657,12 @@ impl ASTLowerer { ))) } }, - _ => {} + _ => { + if let Some(type_spec) = opt_cast_to { + log!(err "cast({type_spec}): {call}"); + self.ctx.cast(type_spec, &mut call)?; + } + } } Ok(call) } @@ -1241,7 +1282,7 @@ impl ASTLowerer { fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult { log!(info "entered {}", fn_name!()); match expr { - ast::Expr::Lit(lit) => Ok(hir::Expr::Lit(hir::Literal::from(lit.token))), + ast::Expr::Lit(lit) => Ok(hir::Expr::Lit(self.lower_literal(lit)?)), ast::Expr::Array(arr) => Ok(hir::Expr::Array(self.lower_array(arr)?)), ast::Expr::Tuple(tup) => Ok(hir::Expr::Tuple(self.lower_tuple(tup)?)), ast::Expr::Record(rec) => Ok(hir::Expr::Record(self.lower_record(rec)?)), diff --git a/compiler/erg_compiler/ownercheck.rs b/compiler/erg_compiler/ownercheck.rs index 0b5ffe3f..f609cb4f 100644 --- a/compiler/erg_compiler/ownercheck.rs +++ b/compiler/erg_compiler/ownercheck.rs @@ -156,6 +156,10 @@ impl OwnershipChecker { self.check_expr(&a.expr, ownership, false); } } + Array::WithLength(arr) => { + self.check_expr(&arr.elem, ownership, false); + self.check_expr(&arr.len, ownership, false); + } _ => todo!(), }, Expr::Tuple(tuple) => match tuple { @@ -182,12 +186,15 @@ impl OwnershipChecker { } } Expr::Set(set) => match set { - hir::Set::Normal(set) => { - for a in set.elems.pos_args.iter() { + hir::Set::Normal(st) => { + for a in st.elems.pos_args.iter() { self.check_expr(&a.expr, ownership, false); } } - hir::Set::WithLength(_) => todo!(), + hir::Set::WithLength(st) => { + self.check_expr(&st.elem, ownership, false); + self.check_expr(&st.len, ownership, false); + } }, // TODO: capturing Expr::Lambda(lambda) => { diff --git a/compiler/erg_compiler/std/_erg_std_prelude.py b/compiler/erg_compiler/std/_erg_std_prelude.py new file mode 100644 index 00000000..0d7d8e03 --- /dev/null +++ b/compiler/erg_compiler/std/_erg_std_prelude.py @@ -0,0 +1,107 @@ +from collections.abc import Iterable, Sequence, Iterator, Container + +def in_operator(x, y): + if type(y) == type: + if isinstance(x, y): + return True + # TODO: trait check + return False + elif (type(y) == list or type(y) == set) and type(y[0]) == type: + # FIXME: + type_check = in_operator(x[0], y[0]) + len_check = len(x) == len(y) + return type_check and len_check + elif type(y) == dict and type(y[0]) == type: + NotImplemented + else: + return x in y + +class Range: + def __init__(self, start, end): + self.start = start + self.end = end + def __contains__(self, item): + pass + def __getitem__(self, item): + pass + def __len__(self): + pass + def __iter__(self): + return RangeIterator(rng=self) + +Sequence.register(Range) +Container.register(Range) +Iterable.register(Range) + +# represents `start<..end` +class LeftOpenRange(Range): + def __contains__(self, item): + return self.start < item <= self.end + def __getitem__(self, item): + return NotImplemented + def __len__(self): + return NotImplemented + +# represents `start.. T or U +cond|T: Type|(c: Bool, then: T, else: T): T = if c: do then do else diff --git a/compiler/erg_parser/Cargo.toml b/compiler/erg_parser/Cargo.toml index 34a898a6..de254b12 100644 --- a/compiler/erg_parser/Cargo.toml +++ b/compiler/erg_parser/Cargo.toml @@ -1,22 +1,22 @@ [package] name = "erg_parser" -version = "0.5.7" description = "The Erg parser" -authors = ["erg-lang team "] -license = "MIT OR Apache-2.0" -edition = "2021" -repository = "https://github.com/erg-lang/erg/tree/main/src/erg_compiler/erg_parser" -documentation = "https://docs.rs/erg_parser" -homepage = "https://erg-lang.github.io/" +documentation = "http://docs.rs/erg_parser" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true [features] -debug = [ "erg_common/debug" ] -japanese = [ "erg_common/japanese" ] -simplified_chinese = [ "erg_common/simplified_chinese" ] -traditional_chinese = [ "erg_common/traditional_chinese" ] +debug = ["erg_common/debug"] +japanese = ["erg_common/japanese"] +simplified_chinese = ["erg_common/simplified_chinese"] +traditional_chinese = ["erg_common/traditional_chinese"] [dependencies] -erg_common = { version = "0.5.7", path = "../erg_common" } +erg_common = { version = "0.5.9-nightly.0", path = "../erg_common" } [lib] path = "lib.rs" diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index b21c2544..d73e2489 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -11,7 +11,7 @@ use erg_common::{ fmt_option, fmt_vec, impl_display_for_enum, impl_display_for_single_struct, impl_display_from_nested, impl_displayable_stream_for_wrapper, impl_locational, impl_locational_for_enum, impl_nested_display_for_chunk_enum, impl_nested_display_for_enum, - impl_stream, impl_stream_for_wrapper, + impl_stream, impl_stream_for_wrapper, option_enum_unwrap, }; use erg_common::{fmt_vec_split_with, Str}; @@ -960,6 +960,32 @@ impl Call { args, } } + + pub fn is_match(&self) -> bool { + self.obj + .get_name() + .map(|s| &s[..] == "match") + .unwrap_or(false) + } + + pub fn is_assert_cast(&self) -> bool { + self.obj + .get_name() + .map(|s| &s[..] == "assert") + .unwrap_or(false) + && self + .args + .get_left_or_key("pred") + .map(|pred| pred.is_bin_in()) + .unwrap_or(false) + } + + pub fn assert_cast_target_type(&self) -> Option<&Expr> { + self.args + .get_left_or_key("pred") + .and_then(|pred| option_enum_unwrap!(pred, Expr::BinOp)) + .map(|bin| bin.args[1].as_ref()) + } } /// e.g. `Data::{x = 1; y = 2}` @@ -3132,7 +3158,11 @@ impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record, impl Expr { pub fn is_match_call(&self) -> bool { - matches!(self, Expr::Call(Call{ obj, .. }) if obj.get_name().map(|s| &s[..] == "match").unwrap_or(false)) + matches!(self, Expr::Call(call) if call.is_match()) + } + + pub fn is_bin_in(&self) -> bool { + matches!(self, Expr::BinOp(bin) if bin.op.is(TokenKind::InOp)) } pub fn is_const_acc(&self) -> bool { diff --git a/compiler/erg_parser/error.rs b/compiler/erg_parser/error.rs index e0696e5a..3007e1f7 100644 --- a/compiler/erg_parser/error.rs +++ b/compiler/erg_parser/error.rs @@ -11,6 +11,18 @@ use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang}; #[derive(Debug)] pub struct LexError(ErrorCore); +impl From for LexError { + fn from(core: ErrorCore) -> Self { + Self(core) + } +} + +impl From for ErrorCore { + fn from(err: LexError) -> Self { + err.0 + } +} + #[derive(Debug)] pub struct LexErrors(Vec); diff --git a/compiler/erg_parser/lex.rs b/compiler/erg_parser/lex.rs index fa2d7b96..7b8a3335 100644 --- a/compiler/erg_parser/lex.rs +++ b/compiler/erg_parser/lex.rs @@ -461,15 +461,30 @@ impl Lexer /*<'a>*/ { let mut num = mantissa; debug_power_assert!(self.peek_cur_ch(), ==, Some('e')); num.push(self.consume().unwrap()); // e - num.push(self.consume().unwrap()); // + | - - while let Some(cur) = self.peek_cur_ch() { - if cur.is_ascii_digit() || cur == '_' { - num.push(self.consume().unwrap()); - } else { - break; + if self.peek_cur_ch().is_some() { + num.push(self.consume().unwrap()); // + | - + while let Some(cur) = self.peek_cur_ch() { + if cur.is_ascii_digit() || cur == '_' { + num.push(self.consume().unwrap()); + } else { + break; + } } + Ok(self.emit_token(RatioLit, &num)) + } else { + let token = self.emit_token(RatioLit, &num); + Err(LexError::syntax_error( + 0, + token.loc(), + switch_lang!( + "japanese" => format!("`{}`は無効な十進数リテラルです", &token.content), + "simplified_chinese" => format!("`{}`是无效的十进制字词", &token.content), + "traditional_chinese" => format!("`{}`是無效的十進製文字", &token.content), + "english" => format!("`{}` is invalid decimal literal", &token.content), + ), + None, + )) } - Ok(self.emit_token(RatioLit, &num)) } /// `_` will be removed at compiletime @@ -851,6 +866,10 @@ impl Iterator for Lexer /*<'a>*/ { ))) } } + Some('-') => { + self.consume(); + self.accept(Inclusion, "<-") + } Some('=') => { self.consume(); self.accept(LessEq, "<=") diff --git a/compiler/erg_parser/parse.rs b/compiler/erg_parser/parse.rs index 6dfe2a99..fd9dd01f 100644 --- a/compiler/erg_parser/parse.rs +++ b/compiler/erg_parser/parse.rs @@ -471,7 +471,8 @@ impl Parser { } } } - Some(t) if t.is(LSqBr) => { + // x[...] (`x [...]` will interpreted as `x([...])`) + Some(t) if t.is(LSqBr) && acc.col_end().unwrap() == t.col_begin().unwrap() => { self.skip(); let index = self .try_reduce_expr(false, false, false) @@ -510,52 +511,6 @@ impl Parser { Ok(acc) } - fn validate_const_expr(&mut self, expr: Expr) -> ParseResult { - match expr { - Expr::Lit(l) => Ok(ConstExpr::Lit(l)), - Expr::Accessor(Accessor::Ident(local)) => { - let local = ConstLocal::new(local.name.into_token()); - Ok(ConstExpr::Accessor(ConstAccessor::Local(local))) - } - Expr::Array(array) => match array { - Array::Normal(arr) => { - let (elems, _, _) = arr.elems.deconstruct(); - let mut const_elems = vec![]; - for elem in elems.into_iter() { - let const_expr = self.validate_const_expr(elem.expr)?; - const_elems.push(ConstPosArg::new(const_expr)); - } - let elems = ConstArgs::new(const_elems, vec![], None); - let const_arr = ConstArray::new(arr.l_sqbr, arr.r_sqbr, elems, None); - Ok(ConstExpr::Array(const_arr)) - } - other => { - self.errs.push(ParseError::feature_error( - line!() as usize, - other.loc(), - "???", - )); - Err(()) - } - }, - // TODO: App, Record, BinOp, UnaryOp, - other => { - self.errs.push(ParseError::syntax_error( - 0, - other.loc(), - switch_lang!( - "japanese" => "この式はコンパイル時計算できないため、型引数には使用できません", - "simplified_chinese" => "此表达式在编译时不可计算,因此不能用作类型参数", - "traditional_chinese" => "此表達式在編譯時不可計算,因此不能用作類型參數", - "english" => "this expression is not computable at the compile-time, so cannot used as a type-argument", - ), - None, - )); - Err(()) - } - } - } - /// For parsing elements of arrays and tuples fn try_reduce_elems(&mut self) -> ParseResult { debug_call_info!(self); @@ -950,9 +905,7 @@ impl Parser { } } let defs = RecordAttrs::from(defs); - let class = self - .convert_rhs_to_type_spec(class) - .map_err(|_| self.stack_dec())?; + let class = Self::expr_to_type_spec(class).map_err(|e| self.errs.push(e))?; self.level -= 1; Ok(Methods::new(class, vis, defs)) } @@ -1036,9 +989,7 @@ impl Parser { let t_spec = self .try_reduce_expr(false, false, false) .map_err(|_| self.stack_dec())?; - let t_spec = self - .convert_rhs_to_type_spec(t_spec) - .map_err(|_| self.stack_dec())?; + let t_spec = Self::expr_to_type_spec(t_spec).map_err(|e| self.errs.push(e))?; let expr = Expr::TypeAsc(TypeAscription::new(lhs, op, t_spec)); stack.push(ExprOrOp::Expr(expr)); } @@ -1280,9 +1231,7 @@ impl Parser { let t_spec = self .try_reduce_expr(false, in_type_args, in_brace) .map_err(|_| self.stack_dec())?; - let t_spec = self - .convert_rhs_to_type_spec(t_spec) - .map_err(|_| self.stack_dec())?; + let t_spec = Self::expr_to_type_spec(t_spec).map_err(|e| self.errs.push(e))?; let expr = Expr::TypeAsc(TypeAscription::new(lhs, op, t_spec)); stack.push(ExprOrOp::Expr(expr)); } @@ -2173,9 +2122,7 @@ impl Parser { pack: DataPack, ) -> ParseResult { debug_call_info!(self); - let class = self - .convert_rhs_to_type_spec(*pack.class) - .map_err(|_| self.stack_dec())?; + let class = Self::expr_to_type_spec(*pack.class).map_err(|e| self.errs.push(e))?; let args = self .convert_record_to_record_pat(pack.args) .map_err(|_| self.stack_dec())?; @@ -2463,7 +2410,7 @@ impl Parser { fn convert_kw_arg_to_default_param(&mut self, arg: KwArg) -> ParseResult { debug_call_info!(self); let pat = ParamPattern::VarName(VarName::new(arg.keyword)); - let expr = self.validate_const_expr(arg.expr)?; + let expr = Self::validate_const_expr(arg.expr).map_err(|e| self.errs.push(e))?; let param = ParamSignature::new(pat, arg.t_spec, Some(expr)); self.level -= 1; Ok(param) @@ -2672,95 +2619,51 @@ impl Parser { TypeBoundSpecs::empty(), )) } +} - fn convert_rhs_to_type_spec(&mut self, rhs: Expr) -> ParseResult { - debug_call_info!(self); - match rhs { - Expr::Accessor(acc) => { - let t_spec = self - .convert_accessor_to_type_spec(acc) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(t_spec) +// The APIs defined below are also used by `ASTLowerer` to interpret expressions as types. +impl Parser { + fn validate_const_expr(expr: Expr) -> Result { + match expr { + Expr::Lit(l) => Ok(ConstExpr::Lit(l)), + Expr::Accessor(Accessor::Ident(local)) => { + let local = ConstLocal::new(local.name.into_token()); + Ok(ConstExpr::Accessor(ConstAccessor::Local(local))) } - Expr::Call(call) => { - let predecl = self - .convert_call_to_predecl_type_spec(call) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::PreDeclTy(predecl)) - } - Expr::Lambda(lambda) => { - let lambda = self - .convert_lambda_to_subr_type_spec(lambda) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::Subr(lambda)) - } - Expr::Array(array) => { - let array = self - .convert_array_to_array_type_spec(array) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::Array(array)) - } - Expr::Set(set) => { - let set = self - .convert_set_to_set_type_spec(set) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::Set(set)) - } - Expr::BinOp(bin) => { - if bin.op.kind.is_range_op() { - let op = bin.op; - let mut args = bin.args.into_iter(); - let lhs = self - .validate_const_expr(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - let rhs = self - .validate_const_expr(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::Interval { op, lhs, rhs }) - } else if bin.op.kind == TokenKind::AndOp { - let mut args = bin.args.into_iter(); - let lhs = self - .convert_rhs_to_type_spec(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - let rhs = self - .convert_rhs_to_type_spec(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::and(lhs, rhs)) - } else if bin.op.kind == TokenKind::OrOp { - let mut args = bin.args.into_iter(); - let lhs = self - .convert_rhs_to_type_spec(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - let rhs = self - .convert_rhs_to_type_spec(*args.next().unwrap()) - .map_err(|_| self.stack_dec())?; - self.level -= 1; - Ok(TypeSpec::or(lhs, rhs)) - } else { - self.level -= 1; - let err = ParseError::simple_syntax_error(line!() as usize, bin.loc()); - self.errs.push(err); - Err(()) + Expr::Array(array) => match array { + Array::Normal(arr) => { + let (elems, _, _) = arr.elems.deconstruct(); + let mut const_elems = vec![]; + for elem in elems.into_iter() { + let const_expr = Self::validate_const_expr(elem.expr)?; + const_elems.push(ConstPosArg::new(const_expr)); + } + let elems = ConstArgs::new(const_elems, vec![], None); + let const_arr = ConstArray::new(arr.l_sqbr, arr.r_sqbr, elems, None); + Ok(ConstExpr::Array(const_arr)) } - } - other => { - self.level -= 1; - let err = ParseError::simple_syntax_error(line!() as usize, other.loc()); - self.errs.push(err); - Err(()) - } + other => Err(ParseError::feature_error( + line!() as usize, + other.loc(), + "???", + )), + }, + // TODO: App, Record, BinOp, UnaryOp, + other => Err(ParseError::syntax_error( + line!() as usize, + other.loc(), + switch_lang!( + "japanese" => "この式はコンパイル時計算できないため、型引数には使用できません", + "simplified_chinese" => "此表达式在编译时不可计算,因此不能用作类型参数", + "traditional_chinese" => "此表達式在編譯時不可計算,因此不能用作類型參數", + "english" => "this expression is not computable at the compile-time, so cannot used as a type-argument", + ), + None, + )), } } - fn convert_accessor_to_type_spec(&mut self, accessor: Accessor) -> ParseResult { - debug_call_info!(self); + fn accessor_to_type_spec(accessor: Accessor) -> Result { let t_spec = match accessor { Accessor::Ident(ident) => { let predecl = @@ -2768,32 +2671,22 @@ impl Parser { TypeSpec::PreDeclTy(predecl) } Accessor::TypeApp(tapp) => { - let spec = self - .convert_rhs_to_type_spec(*tapp.obj) - .map_err(|_| self.stack_dec())?; + let spec = Self::expr_to_type_spec(*tapp.obj)?; TypeSpec::type_app(spec, tapp.type_args) } other => { - self.level -= 1; let err = ParseError::simple_syntax_error(line!() as usize, other.loc()); - self.errs.push(err); - return Err(()); + return Err(err); } }; - self.level -= 1; Ok(t_spec) } - fn convert_call_to_predecl_type_spec(&mut self, _call: Call) -> ParseResult { - debug_call_info!(self); + fn call_to_predecl_type_spec(_call: Call) -> Result { todo!() } - fn convert_lambda_to_subr_type_spec( - &mut self, - mut lambda: Lambda, - ) -> ParseResult { - debug_call_info!(self); + fn lambda_to_subr_type_spec(mut lambda: Lambda) -> Result { let bounds = lambda.sig.bounds; let lparen = lambda.sig.params.parens.map(|(l, _)| l); let mut non_defaults = vec![]; @@ -2838,8 +2731,7 @@ impl Parser { }; defaults.push(param); } - let return_t = self.convert_rhs_to_type_spec(lambda.body.remove(0))?; - self.level -= 1; + let return_t = Self::expr_to_type_spec(lambda.body.remove(0))?; Ok(SubrTypeSpec::new( bounds, lparen, @@ -2851,45 +2743,86 @@ impl Parser { )) } - fn convert_array_to_array_type_spec(&mut self, array: Array) -> ParseResult { - debug_call_info!(self); + fn array_to_array_type_spec(array: Array) -> Result { match array { Array::Normal(arr) => { // TODO: add hint - self.errs - .push(ParseError::simple_syntax_error(line!() as usize, arr.loc())); - Err(()) + let err = ParseError::simple_syntax_error(line!() as usize, arr.loc()); + Err(err) } Array::WithLength(arr) => { - let t_spec = self.convert_rhs_to_type_spec(arr.elem.expr)?; - let len = self.validate_const_expr(*arr.len)?; - self.level -= 1; + let t_spec = Self::expr_to_type_spec(arr.elem.expr)?; + let len = Self::validate_const_expr(*arr.len)?; Ok(ArrayTypeSpec::new(t_spec, len)) } Array::Comprehension(arr) => { // TODO: add hint - self.errs - .push(ParseError::simple_syntax_error(line!() as usize, arr.loc())); - Err(()) + let err = ParseError::simple_syntax_error(line!() as usize, arr.loc()); + Err(err) } } } - fn convert_set_to_set_type_spec(&mut self, set: Set) -> ParseResult { - debug_call_info!(self); + fn set_to_set_type_spec(set: Set) -> Result { match set { Set::Normal(arr) => { // TODO: add hint - self.errs - .push(ParseError::simple_syntax_error(line!() as usize, arr.loc())); - Err(()) + let err = ParseError::simple_syntax_error(line!() as usize, arr.loc()); + Err(err) } Set::WithLength(set) => { - let t_spec = self.convert_rhs_to_type_spec(set.elem.expr)?; - let len = self.validate_const_expr(*set.len)?; - self.level -= 1; + let t_spec = Self::expr_to_type_spec(set.elem.expr)?; + let len = Self::validate_const_expr(*set.len)?; Ok(SetTypeSpec::new(t_spec, len)) } } } + + pub fn expr_to_type_spec(rhs: Expr) -> Result { + match rhs { + Expr::Accessor(acc) => Self::accessor_to_type_spec(acc), + Expr::Call(call) => { + let predecl = Self::call_to_predecl_type_spec(call)?; + Ok(TypeSpec::PreDeclTy(predecl)) + } + Expr::Lambda(lambda) => { + let lambda = Self::lambda_to_subr_type_spec(lambda)?; + Ok(TypeSpec::Subr(lambda)) + } + Expr::Array(array) => { + let array = Self::array_to_array_type_spec(array)?; + Ok(TypeSpec::Array(array)) + } + Expr::Set(set) => { + let set = Self::set_to_set_type_spec(set)?; + Ok(TypeSpec::Set(set)) + } + Expr::BinOp(bin) => { + if bin.op.kind.is_range_op() { + let op = bin.op; + let mut args = bin.args.into_iter(); + let lhs = Self::validate_const_expr(*args.next().unwrap())?; + let rhs = Self::validate_const_expr(*args.next().unwrap())?; + Ok(TypeSpec::Interval { op, lhs, rhs }) + } else if bin.op.kind == TokenKind::AndOp { + let mut args = bin.args.into_iter(); + let lhs = Self::expr_to_type_spec(*args.next().unwrap())?; + let rhs = Self::expr_to_type_spec(*args.next().unwrap())?; + Ok(TypeSpec::and(lhs, rhs)) + } else if bin.op.kind == TokenKind::OrOp { + let mut args = bin.args.into_iter(); + let lhs = Self::expr_to_type_spec(*args.next().unwrap())?; + let rhs = Self::expr_to_type_spec(*args.next().unwrap())?; + Ok(TypeSpec::or(lhs, rhs)) + } else { + let err = ParseError::simple_syntax_error(line!() as usize, bin.loc()); + Err(err) + } + } + other => { + let err = ParseError::simple_syntax_error(line!() as usize, other.loc()); + Err(err) + } + } + } } diff --git a/compiler/erg_parser/token.rs b/compiler/erg_parser/token.rs index 451657aa..0afeb2f8 100644 --- a/compiler/erg_parser/token.rs +++ b/compiler/erg_parser/token.rs @@ -109,6 +109,8 @@ pub enum TokenKind { RefMutOp, /// = Equal, + /// <- + Inclusion, /// := Walrus, /// -> @@ -214,9 +216,8 @@ impl TokenKind { | InfLit => TokenCategory::Literal, PrePlus | PreMinus | PreBitNot | Mutate | RefOp | RefMutOp => TokenCategory::UnaryOp, Try => TokenCategory::PostfixOp, - Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | Walrus => { - TokenCategory::SpecialBinOp - } + Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | Walrus + | Inclusion => TokenCategory::SpecialBinOp, Equal => TokenCategory::DefOp, FuncArrow | ProcArrow => TokenCategory::LambdaOp, Semi | Newline => TokenCategory::Separator, @@ -246,14 +247,14 @@ impl TokenKind { BitOr => 120, // || Closed | LeftOpen | RightOpen | Open => 100, // range operators Less | Gre | LessEq | GreEq | DblEq | NotEq | InOp | NotInOp | IsOp | IsNotOp => 90, // < > <= >= == != in notin is isnot - AndOp => 80, // and - OrOp => 70, // or - FuncArrow | ProcArrow => 60, // -> => - Colon | SupertypeOf | SubtypeOf => 50, // : :> <: - Comma => 40, // , - Equal | Walrus => 20, // = := - Newline | Semi => 10, // \n ; - LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent + AndOp => 80, // and + OrOp => 70, // or + FuncArrow | ProcArrow | Inclusion => 60, // -> => <- + Colon | SupertypeOf | SubtypeOf => 50, // : :> <: + Comma => 40, // , + Equal | Walrus => 20, // = := + Newline | Semi => 10, // \n ; + LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent _ => return None, }; Some(prec) diff --git a/compiler/erg_type/Cargo.toml b/compiler/erg_type/Cargo.toml index 25fb9332..ec54b72b 100644 --- a/compiler/erg_type/Cargo.toml +++ b/compiler/erg_type/Cargo.toml @@ -1,25 +1,25 @@ [package] name = "erg_type" -version = "0.5.7" description = "APIs for Erg types" -authors = ["erg-lang team "] -license = "MIT OR Apache-2.0" -edition = "2021" -repository = "https://github.com/erg-lang/erg/tree/main/compiler/erg_type" -documentation = "https://docs.rs/erg_type" -homepage = "https://erg-lang.github.io/" +documentation = "http://docs.rs/erg_type" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +homepage.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -debug = [ "erg_common/debug" ] -japanese = [ "erg_common/japanese" ] -simplified_chinese = [ "erg_common/simplified_chinese" ] -traditional_chinese = [ "erg_common/traditional_chinese" ] +debug = ["erg_common/debug"] +japanese = ["erg_common/japanese"] +simplified_chinese = ["erg_common/simplified_chinese"] +traditional_chinese = ["erg_common/traditional_chinese"] [dependencies] -erg_common = { version = "0.5.7", path = "../erg_common" } -erg_parser = { version = "0.5.7", path = "../erg_parser" } +erg_common = { version = "0.5.9-nightly.0", path = "../erg_common" } +erg_parser = { version = "0.5.9-nightly.0", path = "../erg_parser" } [lib] path = "lib.rs" diff --git a/compiler/erg_type/constructors.rs b/compiler/erg_type/constructors.rs index 2ee529cb..32da9479 100644 --- a/compiler/erg_type/constructors.rs +++ b/compiler/erg_type/constructors.rs @@ -330,7 +330,17 @@ pub fn callable(param_ts: Vec, return_t: Type) -> Type { #[inline] pub fn builtin_mono>(name: S) -> Type { - Type::BuiltinMono(name.into()) + let name = name.into(); + if cfg!(feature = "debug") { + // do not use for: `Int`, `Nat`, ... + match &name[..] { + "Obj" | "Int" | "Nat" | "Ratio" | "Float" | "Bool" | "Str" | "NoneType" | "Code" + | "Frame" | "Error" | "Inf" | "NegInf" | "Type" | "ClassType" | "TraitType" + | "Patch" | "NotImplemented" | "Ellipsis" | "Never" => todo!("{name}"), + _ => {} + } + } + Type::BuiltinMono(name) } #[inline] diff --git a/compiler/erg_type/free.rs b/compiler/erg_type/free.rs index 88eed937..82b79f09 100644 --- a/compiler/erg_type/free.rs +++ b/compiler/erg_type/free.rs @@ -325,6 +325,17 @@ impl FreeKind { } } + pub fn new_unbound(lev: Level, constraint: Constraint) -> Self { + UNBOUND_ID.with(|id| { + *id.borrow_mut() += 1; + Self::Unbound { + id: *id.borrow(), + lev, + constraint, + } + }) + } + pub const fn named_unbound(name: Str, lev: Level, constraint: Constraint) -> Self { Self::NamedUnbound { name, @@ -440,6 +451,14 @@ impl Free { *self.borrow_mut() = FreeKind::Linked(to.clone()); } + pub fn replace(&self, to: FreeKind) { + // prevent linking to self + if self.is_linked() && addr_eq!(*self.borrow(), to) { + return; + } + *self.borrow_mut() = to; + } + /// NOTE: Do not use this except to rewrite circular references. /// No reference to any type variable may be left behind when rewriting. /// However, `get_bound_types` is safe because it does not return references. diff --git a/compiler/erg_type/lib.rs b/compiler/erg_type/lib.rs index 1aa7a7d8..65dbd745 100644 --- a/compiler/erg_type/lib.rs +++ b/compiler/erg_type/lib.rs @@ -1284,15 +1284,9 @@ impl PartialEq for Type { rhs: rrhs, }, ) => lhs == rlhs && rhs == rrhs, + (Self::FreeVar(fv), other) if fv.is_linked() => &*fv.crack() == other, + (_self, Self::FreeVar(fv)) if fv.is_linked() => _self == &*fv.crack(), (Self::FreeVar(l), Self::FreeVar(r)) => l == r, - (Self::FreeVar(fv), other) => match &*fv.borrow() { - FreeKind::Linked(t) => t == other, - _ => false, - }, - (self_, Self::FreeVar(fv)) => match &*fv.borrow() { - FreeKind::Linked(t) => t == self_, - _ => false, - }, (Self::Failure, Self::Failure) | (Self::Uninited, Self::Uninited) => true, _ => false, } @@ -1922,11 +1916,6 @@ impl Type { matches!(self, Self::FreeVar(_)) } - /// FIXME: `Int or Str` should be monomorphic - pub fn is_monomorphic(&self) -> bool { - matches!(self.typarams_len(), Some(0) | None) - } - pub const fn is_callable(&self) -> bool { matches!(self, Self::Subr { .. } | Self::Callable { .. }) } @@ -1935,6 +1924,18 @@ impl Type { matches!(self, Self::FreeVar(fv) if fv.is_unbound() || fv.crack().is_unbound_var()) } + /// See also: `is_monomorphized` + pub fn is_monomorphic(&self) -> bool { + matches!(self.typarams_len(), Some(0) | None) + } + + /// `Set(Int, 3)` is not monomorphic but monomorphized + pub fn is_monomorphized(&self) -> bool { + matches!(self.typarams_len(), Some(0) | None) + || (self.has_no_qvar() && self.has_no_unbound_var()) + } + + /// if the type is polymorphic pub fn has_qvar(&self) -> bool { match self { Self::MonoQVar(_) | Self::PolyQVar { .. } => true, @@ -1972,6 +1973,10 @@ impl Type { } } + pub fn has_no_qvar(&self) -> bool { + !self.has_qvar() + } + pub fn is_cachable(&self) -> bool { match self { Self::FreeVar(_) => false, diff --git a/compiler/erg_type/value.rs b/compiler/erg_type/value.rs index eb951748..4328d4ef 100644 --- a/compiler/erg_type/value.rs +++ b/compiler/erg_type/value.rs @@ -406,27 +406,35 @@ impl ValueObj { matches!(self, Self::Mut(_)) } - pub fn from_str(t: Type, content: Str) -> Self { + pub fn from_str(t: Type, content: Str) -> Option { match t { - Type::Int => Self::Int(content.replace('_', "").parse::().unwrap()), - Type::Nat => Self::Nat(content.replace('_', "").parse::().unwrap()), - Type::Float => Self::Float(content.replace('_', "").parse::().unwrap()), + Type::Int => content.replace('_', "").parse::().ok().map(Self::Int), + Type::Nat => content.replace('_', "").parse::().ok().map(Self::Nat), + Type::Float => content + .replace('_', "") + .parse::() + .ok() + .map(Self::Float), // TODO: - Type::Ratio => Self::Float(content.replace('_', "").parse::().unwrap()), + Type::Ratio => content + .replace('_', "") + .parse::() + .ok() + .map(Self::Float), Type::Str => { if &content[..] == "\"\"" { - Self::Str(Str::from("")) + Some(Self::Str(Str::from(""))) } else { let replaced = content.trim_start_matches('\"').trim_end_matches('\"'); - Self::Str(Str::rc(replaced)) + Some(Self::Str(Str::rc(replaced))) } } - Type::Bool => Self::Bool(&content[..] == "True"), - Type::NoneType => Self::None, - Type::Ellipsis => Self::Ellipsis, - Type::NotImplemented => Self::NotImplemented, - Type::Inf => Self::Inf, - Type::NegInf => Self::NegInf, + Type::Bool => Some(Self::Bool(&content[..] == "True")), + Type::NoneType => Some(Self::None), + Type::Ellipsis => Some(Self::Ellipsis), + Type::NotImplemented => Some(Self::NotImplemented), + Type::Inf => Some(Self::Inf), + Type::NegInf => Some(Self::NegInf), _ => todo!("{t} {content}"), } } @@ -534,7 +542,7 @@ impl ValueObj { Self::None => Type::NoneType, other => panic!("{other} object cannot be mutated"), }, - Self::Illegal => todo!(), + Self::Illegal => Type::Failure, } } diff --git a/doc/EN/dev_guide/troubleshooting.md b/doc/EN/dev_guide/troubleshooting.md new file mode 100644 index 00000000..1e38f910 --- /dev/null +++ b/doc/EN/dev_guide/troubleshooting.md @@ -0,0 +1,9 @@ +# Troubleshooting + +## Q: Local builds succeed, but GitHub Actions builds fail + +A: The branch you are working on may not be following the changes in `main`. + +## Q: The pre-commit check fails + +A: Try committing again. It may fail the first time. If it fails again and again, the code may contain a bug. diff --git a/doc/JA/dev_guide/troubleshooting.md b/doc/JA/dev_guide/troubleshooting.md new file mode 100644 index 00000000..7bcae8e0 --- /dev/null +++ b/doc/JA/dev_guide/troubleshooting.md @@ -0,0 +1,12 @@ +# トラブルシューティング + +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/dev_guide/troubleshooting.md%26commit_hash%3De033e4942e70657008427f05def2d1b1bfb5ed66)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/dev_guide/troubleshooting.md&commit_hash=e033e4942e70657008427f05def2d1b1bfb5ed66) + + +## Q: ローカルでのビルドは成功したが、GitHub Actionsのビルドが失敗する + +A: あなたの作業しているブランチが`main`の変更に追従していない可能性があります。 + +## Q: pre-commitのチェックが失敗する + +A: もう一度コミットを試みてください。最初の1回は失敗することがあります。何度やっても失敗する場合、コードにバグが含まれている可能性があります。 diff --git a/doc/zh_CN/dev_guide/troubleshooting.md b/doc/zh_CN/dev_guide/troubleshooting.md new file mode 100644 index 00000000..47a80562 --- /dev/null +++ b/doc/zh_CN/dev_guide/troubleshooting.md @@ -0,0 +1,11 @@ +# 故障诊断 + +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/dev_guide/troubleshooting.md%26commit_hash%3De033e4942e70657008427f05def2d1b1bfb5ed66)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/dev_guide/troubleshooting.md&commit_hash=e033e4942e70657008427f05def2d1b1bfb5ed66) + +## Q: 本地生成成功, 但 GitHub Actions 生成失败 + +A: 您正在处理的分支可能没有Pull`main`中的更改 + +## Q: 提交前检查失败 + +A: 尝试再次提交, 第一次可能会误判, 如果一次又一次的失败, 那么你的代码可能包含错误 diff --git a/doc/zh_TW/dev_guide/troubleshooting.md b/doc/zh_TW/dev_guide/troubleshooting.md new file mode 100644 index 00000000..1ac7b4d8 --- /dev/null +++ b/doc/zh_TW/dev_guide/troubleshooting.md @@ -0,0 +1,11 @@ +# 故障診斷 + +[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/dev_guide/troubleshooting.md%26commit_hash%3De033e4942e70657008427f05def2d1b1bfb5ed66)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/dev_guide/troubleshooting.md&commit_hash=e033e4942e70657008427f05def2d1b1bfb5ed66) + +## Q: 本地生成成功, 但 GitHub Actions 生成失敗 + +A: 您正在處理的分支可能沒有Pull`main`中的更改 + +## Q: 提交前檢查失敗 + +A: 嘗試再次提交, 第一次可能會誤判, 如果一次又一次的失敗, 那麼你的代碼可能包含錯誤 diff --git a/examples/assert_cast.er b/examples/assert_cast.er new file mode 100644 index 00000000..9d2db00b --- /dev/null +++ b/examples/assert_cast.er @@ -0,0 +1,3 @@ +i: Int = 2 - 1 +assert i in Nat +i: Nat diff --git a/src/dummy.rs b/src/dummy.rs index 5efe0eff..798412a4 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -109,6 +109,9 @@ impl Runnable for DummyVM { .replace(".er", ".pyc"); self.compiler .compile_and_dump_as_pyc(&filename, self.input().read(), "exec")?; + if self.cfg().dump_as_pyc { + return Ok(0); + } let code = exec_pyc(&filename); remove_file(&filename).unwrap(); Ok(code.unwrap_or(1)) diff --git a/tests/fizzbuzz.er b/tests/fizzbuzz.er index afcf9b73..b725528e 100644 --- a/tests/fizzbuzz.er +++ b/tests/fizzbuzz.er @@ -1,6 +1,6 @@ for! 1..<100, i => - match (i % 3, i % 5): - (0, 0) => print! "FizzBuzz" - (0, _) => print! "Fizz" - (_, 0) => print! "Buzz" - (_, _) => print! i + match [i % 3, i % 5]: + [0, 0] => print! "FizzBuzz" + [0, _] => print! "Fizz" + [_, 0] => print! "Buzz" + [_, _] => print! i diff --git a/tests/test.rs b/tests/test.rs index 1ebdb003..90ca0349 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -11,6 +11,11 @@ fn exec_addition() -> Result<(), ()> { expect_failure("tests/addition.er") } +#[test] +fn exec_assert_cast() -> Result<(), ()> { + expect_success("examples/assert_cast.er") +} + #[test] fn exec_class() -> Result<(), ()> { expect_success("examples/class.er") @@ -51,6 +56,11 @@ fn exec_move_check() -> Result<(), ()> { expect_failure("examples/move_check.er") } +#[test] +fn exec_prelude() -> Result<(), ()> { + expect_success("compiler/erg_compiler/std/prelude.er") +} + #[test] fn exec_quantified() -> Result<(), ()> { expect_success("examples/quantified.er")