Merge branch 'main' into dict

This commit is contained in:
Shunsuke Shibayama 2022-10-10 13:57:08 +09:00
commit c784ba261e
53 changed files with 1336 additions and 818 deletions

View file

@ -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"

View file

@ -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.

58
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View file

@ -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`)

25
Cargo.lock generated
View file

@ -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",

View file

@ -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 <moderation.erglang@gmail.com>"]
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 <moderation.erglang@gmail.com>"]
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"]

View file

@ -4,7 +4,7 @@
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>This is the main source code repository for [Erg](https://erg-lang.github.io/). This contains the compiler and documentation.
<br>This is the main source code repository for [Erg](https://erg-lang.org/). This contains the compiler and documentation.
<p align='center'>
<a href="https://github.com/erg-lang/erg/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/erg-lang/erg.svg"></a>

View file

@ -4,7 +4,7 @@
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>こちらは[Erg](https://mtshiba.github.io/TheErgBook)のメインリポジトリです。コンパイラとドキュメントが置かれています。
<br>こちらは[Erg](https://erg-lang.org/)のメインリポジトリです。コンパイラとドキュメントが置かれています。
<p align='center'>
<a href="https://github.com/erg-lang/erg/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/erg-lang/erg.svg"></a>
@ -12,8 +12,8 @@
<br>
</p>
[![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はこんな人におすすめです&#58;

View file

@ -4,7 +4,7 @@
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>这是[Erg](https://erg-lang.github.io/)的主要源代码库。它包含编译器和文档。
<br>这是[Erg](https://erg-lang.org/)的主要源代码库。它包含编译器和文档。
<p align='center'>
<a href="https://github.com/erg-lang/erg/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/erg-lang/erg.svg"></a>
@ -12,8 +12,8 @@
<br>
</p>
[![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可以推荐给以下人员&colon;

View file

@ -4,7 +4,7 @@
<img width="500" src="./assets/erg_logo_with_slogan.svg">
</div>
<br>這是[Erg](https://erg-lang.github.io/)的主要源代碼庫。它包含編譯器和文檔。
<br>這是[Erg](https://erg-lang.org/)的主要源代碼庫。它包含編譯器和文檔。
<p align='center'>
<a href="https://github.com/erg-lang/erg/releases"><img alt="Build status" src="https://img.shields.io/github/v/release/erg-lang/erg.svg"></a>
@ -12,8 +12,8 @@
<br>
</p>
[![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可以推薦給以下人員&colon;

View file

@ -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`

View file

@ -1,13 +1,13 @@
[package]
name = "erg_common"
version = "0.5.7"
description = "A common components library of Erg"
authors = ["erg-lang team <moderation.erglang@gmail.com>"]
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"

View file

@ -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::<u8>().unwrap();
"-o" | "--opt-level" | "--optimization-level" => {
cfg.opt_level = args
.next()
.expect("the value of `-o` is not passed")
.parse::<u8>()
.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::<u32>() {
cfg.python_ver = Some(ver)
}
}
"--py-server-timeout" if next_arg.is_some() => {
if let Ok(time) = next_arg.unwrap().parse::<u64>() {
cfg.py_server_timeout = time;
"-p" | "--py-ver" | "--python-version" => {
let py_ver = args
.next()
.expect("the value of `-p` is not passed")
.parse::<u32>()
.expect("the value of `-p` is not a number");
cfg.python_ver = Some(py_ver);
}
"--py-server-timeout" => {
cfg.py_server_timeout = args
.next()
.expect("the value of `--py-server-timeout` is not passed")
.parse::<u64>()
.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::<u8>() {
cfg.verbose = vr;
}
"--verbose" => {
cfg.verbose = args
.next()
.expect("the value of `--verbose` is not passed")
.parse::<u8>()
.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();

View file

@ -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

View file

@ -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;

View file

@ -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<S: AsRawFd> IsTty for S {
fn is_tty(&self) -> bool {
let fd = self.as_raw_fd();
unsafe { libc::isatty(fd) == 1 }
}
}
#[cfg(target_os = "hermit")]
impl<S: AsRawFd> 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<S: AsRawFd> 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<S: AsRawHandle> 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
}
}

View file

@ -1,25 +1,34 @@
[package]
name = "erg_compiler"
version = "0.5.7"
description = "Centimetre: the Erg compiler"
authors = ["erg-lang team <moderation.erglang@gmail.com>"]
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"

View file

@ -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(())
}

View file

@ -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")),
)],
);
}

View file

@ -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)) {

View file

@ -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<ValueObj> {
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<ValueObj> {
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<ValueObj> {
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),

View file

@ -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"))])) },

View file

@ -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<impl Iterator<Item = &'a Context>> {
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<impl Iterator<Item = &'a Context>> {
// 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<Vec<&'a Context>> {
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<impl Iterator<Item = Type>> {
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() => {

View file

@ -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<Lit>
_ => {
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)),
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<TyParam> {
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| {
// TODO: エラー処理(リテラルでない)はパーサーにやらせる
TypeSpec::Enum(set) => {
let mut new_set = set! {};
for arg in set.pos_args() {
if let ast::ConstExpr::Lit(lit) = &arg.expr {
eval_lit(lit)
new_set.insert(self.eval_lit(lit)?);
} else {
todo!()
}
})
.collect::<Set<_>>(),
)),
}
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)")

View file

@ -281,6 +281,7 @@ pub enum OperationKind {
Import,
PyImport,
Del,
AssertCast,
}
impl OperationKind {

View file

@ -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(())
}
}

View file

@ -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!()

View file

@ -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<AtomicStr>,
) -> 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)]

View file

@ -47,14 +47,16 @@ impl Locational for Literal {
}
}
impl From<Token> for Literal {
fn from(token: Token) -> Self {
let data = ValueObj::from_str(type_from_token_kind(token.kind), token.content.clone());
Self {
impl TryFrom<Token> for Literal {
type Error = ();
fn try_from(token: Token) -> Result<Self, ()> {
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 {

View file

@ -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 {

View file

@ -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<hir::Literal> {
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<hir::Array> {
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<hir::Call> {
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<hir::Expr> {
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)?)),

View file

@ -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) => {

View file

@ -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..<end`
class RightOpenRange(Range):
def __contains__(self, item):
return self.start <= item < self.end
def __getitem__(self, item):
return NotImplemented
def __len__(self):
return NotImplemented
# represents `start<..<end`
class OpenRange(Range):
def __contains__(self, item):
return self.start < item < self.end
def __getitem__(self, item):
return NotImplemented
def __len__(self):
return NotImplemented
# represents `start..end`
class ClosedRange(Range):
def __contains__(self, item):
return self.start <= item <= self.end
def __getitem__(self, item):
return NotImplemented
def __len__(self):
return NotImplemented
class RangeIterator:
def __init__(self, rng):
self.rng = rng
self.needle = self.rng.start
if type(self.rng.start) == int:
if not(self.needle in self.rng):
self.needle += 1
elif type(self.rng.start) == str:
if not(self.needle in self.rng):
self.needle = chr(ord(self.needle) + 1)
else:
if not(self.needle in self.rng):
self.needle = self.needle.incremented()
def __iter__(self):
return self
def __next__(self):
if type(self.rng.start) == int:
if self.needle in self.rng:
result = self.needle
self.needle += 1
return result
elif type(self.rng.start) == str:
if self.needle in self.rng:
result = self.needle
self.needle = chr(ord(self.needle) + 1)
return result
else:
if self.needle in self.rng:
result = self.needle
self.needle = self.needle.incremented()
return result
raise StopIteration
Iterator.register(RangeIterator)

View file

@ -2,7 +2,8 @@ discard _x = None
discard 1
cond c, then, else =
# if: |T, U|(Bool, T, U) -> T or U
cond|T: Type|(c: Bool, then: T, else: T): T =
if c:
do then
do else

View file

@ -1,22 +1,22 @@
[package]
name = "erg_parser"
version = "0.5.7"
description = "The Erg parser"
authors = ["erg-lang team <moderation.erglang@gmail.com>"]
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"

View file

@ -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 {

View file

@ -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<ErrorCore> for LexError {
fn from(core: ErrorCore) -> Self {
Self(core)
}
}
impl From<LexError> for ErrorCore {
fn from(err: LexError) -> Self {
err.0
}
}
#[derive(Debug)]
pub struct LexErrors(Vec<LexError>);

View file

@ -461,6 +461,7 @@ impl Lexer /*<'a>*/ {
let mut num = mantissa;
debug_power_assert!(self.peek_cur_ch(), ==, Some('e'));
num.push(self.consume().unwrap()); // e
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 == '_' {
@ -470,6 +471,20 @@ impl Lexer /*<'a>*/ {
}
}
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,
))
}
}
/// `_` 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, "<=")

View file

@ -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<ConstExpr> {
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<ArrayInner> {
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<VarDataPackPattern> {
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<ParamSignature> {
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<TypeSpec> {
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<ConstExpr, ParseError> {
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::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));
}
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(())
}
}
other => {
self.level -= 1;
let err = ParseError::simple_syntax_error(line!() as usize, other.loc());
self.errs.push(err);
Err(())
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 => 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<TypeSpec> {
debug_call_info!(self);
fn accessor_to_type_spec(accessor: Accessor) -> Result<TypeSpec, ParseError> {
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<PreDeclTypeSpec> {
debug_call_info!(self);
fn call_to_predecl_type_spec(_call: Call) -> Result<PreDeclTypeSpec, ParseError> {
todo!()
}
fn convert_lambda_to_subr_type_spec(
&mut self,
mut lambda: Lambda,
) -> ParseResult<SubrTypeSpec> {
debug_call_info!(self);
fn lambda_to_subr_type_spec(mut lambda: Lambda) -> Result<SubrTypeSpec, ParseError> {
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<ArrayTypeSpec> {
debug_call_info!(self);
fn array_to_array_type_spec(array: Array) -> Result<ArrayTypeSpec, ParseError> {
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<SetTypeSpec> {
debug_call_info!(self);
fn set_to_set_type_spec(set: Set) -> Result<SetTypeSpec, ParseError> {
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<TypeSpec, ParseError> {
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)
}
}
}
}

View file

@ -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,
@ -248,7 +249,7 @@ impl TokenKind {
Less | Gre | LessEq | GreEq | DblEq | NotEq | InOp | NotInOp | IsOp | IsNotOp => 90, // < > <= >= == != in notin is isnot
AndOp => 80, // and
OrOp => 70, // or
FuncArrow | ProcArrow => 60, // -> =>
FuncArrow | ProcArrow | Inclusion => 60, // -> => <-
Colon | SupertypeOf | SubtypeOf => 50, // : :> <:
Comma => 40, // ,
Equal | Walrus => 20, // = :=

View file

@ -1,25 +1,25 @@
[package]
name = "erg_type"
version = "0.5.7"
description = "APIs for Erg types"
authors = ["erg-lang team <moderation.erglang@gmail.com>"]
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"

View file

@ -330,7 +330,17 @@ pub fn callable(param_ts: Vec<Type>, return_t: Type) -> Type {
#[inline]
pub fn builtin_mono<S: Into<Str>>(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]

View file

@ -325,6 +325,17 @@ impl<T> FreeKind<T> {
}
}
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<T: Clone + HasLevel> Free<T> {
*self.borrow_mut() = FreeKind::Linked(to.clone());
}
pub fn replace(&self, to: FreeKind<T>) {
// 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.

View file

@ -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,

View file

@ -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<Self> {
match t {
Type::Int => Self::Int(content.replace('_', "").parse::<i32>().unwrap()),
Type::Nat => Self::Nat(content.replace('_', "").parse::<u64>().unwrap()),
Type::Float => Self::Float(content.replace('_', "").parse::<f64>().unwrap()),
Type::Int => content.replace('_', "").parse::<i32>().ok().map(Self::Int),
Type::Nat => content.replace('_', "").parse::<u64>().ok().map(Self::Nat),
Type::Float => content
.replace('_', "")
.parse::<f64>()
.ok()
.map(Self::Float),
// TODO:
Type::Ratio => Self::Float(content.replace('_', "").parse::<f64>().unwrap()),
Type::Ratio => content
.replace('_', "")
.parse::<f64>()
.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,
}
}

View file

@ -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.

View file

@ -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回は失敗することがあります。何度やっても失敗する場合、コードにバグが含まれている可能性があります。

View file

@ -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: 尝试再次提交, 第一次可能会误判, 如果一次又一次的失败, 那么你的代码可能包含错误

View file

@ -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: 嘗試再次提交, 第一次可能會誤判, 如果一次又一次的失敗, 那麼你的代碼可能包含錯誤

3
examples/assert_cast.er Normal file
View file

@ -0,0 +1,3 @@
i: Int = 2 - 1
assert i in Nat
i: Nat

View file

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

View file

@ -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

View file

@ -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")