diff --git a/Cargo.lock b/Cargo.lock index ecc64be5..237e2ca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "els" -version = "0.1.28-nightly.7" +version = "0.1.28" dependencies = [ "erg_common", "erg_compiler", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "erg" -version = "0.6.16-nightly.7" +version = "0.6.16" dependencies = [ "els", "erg_common", @@ -115,7 +115,7 @@ dependencies = [ [[package]] name = "erg_common" -version = "0.6.16-nightly.7" +version = "0.6.16" dependencies = [ "backtrace-on-stack-overflow", "crossterm", @@ -125,7 +125,7 @@ dependencies = [ [[package]] name = "erg_compiler" -version = "0.6.16-nightly.7" +version = "0.6.16" dependencies = [ "erg_common", "erg_parser", @@ -133,7 +133,7 @@ dependencies = [ [[package]] name = "erg_parser" -version = "0.6.16-nightly.7" +version = "0.6.16" dependencies = [ "erg_common", "unicode-xid", diff --git a/Cargo.toml b/Cargo.toml index 7a243a32..91299245 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ ] [workspace.package] -version = "0.6.16-nightly.7" +version = "0.6.16" authors = ["erg-lang team "] license = "MIT OR Apache-2.0" edition = "2021" @@ -64,10 +64,10 @@ full = ["els", "full-repl", "unicode", "pretty"] experimental = ["erg_common/experimental", "erg_parser/experimental", "erg_compiler/experimental"] [workspace.dependencies] -erg_common = { version = "0.6.16-nightly.7", path = "./crates/erg_common" } -erg_parser = { version = "0.6.16-nightly.7", path = "./crates/erg_parser" } -erg_compiler = { version = "0.6.16-nightly.7", path = "./crates/erg_compiler" } -els = { version = "0.1.28-nightly.7", path = "./crates/els" } +erg_common = { version = "0.6.16", path = "./crates/erg_common" } +erg_parser = { version = "0.6.16", path = "./crates/erg_parser" } +erg_compiler = { version = "0.6.16", path = "./crates/erg_compiler" } +els = { version = "0.1.28", path = "./crates/els" } [dependencies] erg_common = { workspace = true } diff --git a/crates/els/Cargo.toml b/crates/els/Cargo.toml index 9e9eaefd..b49a1c59 100644 --- a/crates/els/Cargo.toml +++ b/crates/els/Cargo.toml @@ -2,7 +2,7 @@ name = "els" description = "An Erg compiler frontend for IDEs, implements LSP." documentation = "http://docs.rs/els" -version = "0.1.28-nightly.7" +version = "0.1.28" authors.workspace = true license.workspace = true edition.workspace = true diff --git a/crates/els/completion.rs b/crates/els/completion.rs index 0409fd7b..43b1f87c 100644 --- a/crates/els/completion.rs +++ b/crates/els/completion.rs @@ -355,24 +355,44 @@ impl CompletionCache { move || { crate::_log!("load_modules"); let major_mods = [ + "argparse", + "array", + "asyncio", + "base64", "datetime", + "decimal", + "fraction", "glob", + "html", "http", "http/client", "http/server", "io", "json", + "logging", "math", "os", "os/path", + "pathlib", + "platform", "random", "re", + "shutil", + "socket", + "sqlite3", + "ssl", + "string", + "subprocess", "sys", + "tempfile", "time", + "timeit", + "unittest", "urllib", + "zipfile", ]; #[cfg(feature = "py_compat")] - let py_specific_mods = ["typing", "collections/abc"]; + let py_specific_mods = ["dataclasses", "typing", "collections/abc"]; #[cfg(not(feature = "py_compat"))] let py_specific_mods = []; let ext_mods = ["numpy", "pandas", "matplotlib", "matplotlib/pyplot"]; diff --git a/crates/els/diff.rs b/crates/els/diff.rs index 8f5a71b8..bc1a52d5 100644 --- a/crates/els/diff.rs +++ b/crates/els/diff.rs @@ -124,10 +124,14 @@ impl HIRDiff { old.module.insert(idx, expr); } Self::Deletion(usize) => { - old.module.remove(usize); + if old.module.get(usize).is_some() { + old.module.remove(usize); + } } Self::Modification(idx, expr) => { - *old.module.get_mut(idx).unwrap() = expr; + if let Some(old_expr) = old.module.get_mut(idx) { + *old_expr = expr; + } } Self::Nop => {} } diff --git a/crates/erg_compiler/context/eval.rs b/crates/erg_compiler/context/eval.rs index 443a2659..5b11b862 100644 --- a/crates/erg_compiler/context/eval.rs +++ b/crates/erg_compiler/context/eval.rs @@ -48,7 +48,7 @@ pub fn type_from_token_kind(kind: TokenKind) -> Type { use TokenKind::*; match kind { - NatLit => Type::Nat, + NatLit | BinLit | OctLit | HexLit => Type::Nat, IntLit => Type::Int, RatioLit => Type::Ratio, StrLit | DocComment => Type::Str, diff --git a/crates/erg_compiler/lib/pystd/os.d/__init__.d.er b/crates/erg_compiler/lib/pystd/os.d/__init__.d.er index aabc894c..6661f54a 100644 --- a/crates/erg_compiler/lib/pystd/os.d/__init__.d.er +++ b/crates/erg_compiler/lib/pystd/os.d/__init__.d.er @@ -8,6 +8,7 @@ The name of the operating system dependent module imported. The following names .name: Str .chdir!: (path: PathLike, ) => NoneType +.chmod!: (path: PathLike, mode: Nat) => NoneType .getcwd!: () => Str .getenv!: (key: Str, default: Str or NoneType := NoneType) => Str .listdir!: (path := PathLike,) => [Str; _] diff --git a/crates/erg_compiler/lib/pystd/pathlib.d.er b/crates/erg_compiler/lib/pystd/pathlib.d.er index fa9481a1..6286dc4a 100644 --- a/crates/erg_compiler/lib/pystd/pathlib.d.er +++ b/crates/erg_compiler/lib/pystd/pathlib.d.er @@ -1,7 +1,37 @@ .PurePath: ClassType -.PurePath.parts: [Str; _] +.PurePath. + parts: [Str; _] + drive: Str + root: Str + anchor: Str + parents: [.PurePath; _] + parent: .PurePath + name: Str + suffix: Str + suffixes: [Str; _] + stem: Str + __call__: (*segments: Str) -> .PurePath + as_posix: (self: .PurePath) -> Str + as_uri: (self: .PurePath) -> Str + is_absolute: (self: .PurePath) -> Bool + is_relative_to: (self: .PurePath, *other: .PurePath) -> Bool + is_reserved: (self: .PurePath) -> Bool + joinpath: (self: .PurePath, *other: .PurePath) -> .PurePath + match: (self: .PurePath, pattern: Str) -> Bool + relative_to: (self: .PurePath, *other: .PurePath) -> .PurePath + with_name: (self: .PurePath, name: Str) -> .PurePath + with_stem: (self: .PurePath, suffix: Str) -> .PurePath + with_suffix: (self: .PurePath, suffix: Str) -> .PurePath .PurePosixPath: ClassType .PureWindowsPath: ClassType .Path: ClassType +.Path <: .PurePath +.Path. + __call__: (*segments: Str) -> .Path + cwd!: () => .Path + home!: () => .Path + samefile!: (self: .Path, other: .Path) => Bool + open!: (self: .Path, mode := Str) => File! + chmod!: (self: .Path, mode: Nat) => NoneType .PosixPath: ClassType .WindowsPath: ClassType diff --git a/crates/erg_compiler/lib/pystd/stat.d.er b/crates/erg_compiler/lib/pystd/stat.d.er new file mode 100644 index 00000000..e729b76e --- /dev/null +++ b/crates/erg_compiler/lib/pystd/stat.d.er @@ -0,0 +1,88 @@ +.ST_MMODE: {0} +.ST_INO: {1} +.ST_DEV: {2} +.ST_NLINK: {3} +.ST_UID: {4} +.ST_GID: {5} +.ST_SIZE: {6} +.ST_ATIME: {7} +.ST_MTIME: {8} +.ST_CTIME: {9} + +.S_IMODE: (mode: Nat) -> Nat +.S_IFMT: (mode: Nat) -> Nat + +.S_IFDIR: {0o04000} +.S_IFCHR: {0o02000} +.S_IFBLK: {0o06000} +.S_IFREG: {0o10000} +.S_IFIFO: {0o01000} +.S_IFLNK: {0o12000} +.S_IFSOCK: {0o14000} +.S_IFDOOR: {0} +.S_IFPORT: {0} +.S_IFWHT: {0} + +.S_ISDIR: (mode: Nat) -> Bool +.S_ISCHR: (mode: Nat) -> Bool +.S_ISBLK: (mode: Nat) -> Bool +.S_ISREG: (mode: Nat) -> Bool +.S_ISFIFO: (mode: Nat) -> Bool +.S_ISLNK: (mode: Nat) -> Bool +.S_ISSOCK: (mode: Nat) -> Bool +.S_ISDOOR: (mode: Nat) -> Bool +.S_ISPORT: (mode: Nat) -> Bool +.S_ISWHT: (mode: Nat) -> Bool + +.S_ISUID: {0o4000} +.S_ISGID: {0o2000} +.S_ENFMT: {0o2000} +.S_ISVTX: {0o1000} +.S_IREAD: {0o0400} +.S_IWRITE: {0o0200} +.S_IEXEC: {0o0100} +.S_IRWXU: {0o0700} +.S_IRUSR: {0o0400} +.S_IWUSR: {0o0200} +.S_IXUSR: {0o0100} +.S_IRWXG: {0o0070} +.S_IRGRP: {0o0040} +.S_IWGRP: {0o0020} +.S_IXGRP: {0o0010} +.S_IRWXO: {0o0007} +.S_IROTH: {0o0004} +.S_IWOTH: {0o0002} +.S_IXOTH: {0o0001} + +.UF_NODUMP: {0x00000001} +.UF_IMMUTABLE: {0x00000002} +.UF_APPEND: {0x00000004} +.UF_OPAQUE: {0x00000008} +.UF_NOUNLINK: {0x00000010} +.UF_COMPRESSED: {0x00000020} +.UF_HIDDEN: {0x00008000} +.SF_ARCHIVED: {0x00010000} +.SF_IMMUTABLE: {0x00020000} +.SF_APPEND: {0x00040000} +.SF_NOUNLINK: {0x00100000} +.SF_SNAPSHOT: {0x00200000} + +.filemode: (mode: Nat) -> Str + +.FILE_ATTRIBUTE_ARCHIVE: {32} +.FILE_ATTRIBUTE_COMPRESSED: {2048} +.FILE_ATTRIBUTE_DEVICE: {64} +.FILE_ATTRIBUTE_DIRECTORY: {16} +.FILE_ATTRIBUTE_ENCRYPTED: {16384} +.FILE_ATTRIBUTE_HIDDEN: {2} +.FILE_ATTRIBUTE_INTEGRITY_STREAM: {32768} +.FILE_ATTRIBUTE_NORMAL: {128} +.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED: {8192} +.FILE_ATTRIBUTE_NO_SCRUB_DATA: {131072} +.FILE_ATTRIBUTE_OFFLINE: {4096} +.FILE_ATTRIBUTE_READONLY: {1} +.FILE_ATTRIBUTE_REPARSE_POINT: {1024} +.FILE_ATTRIBUTE_SPARSE_FILE: {512} +.FILE_ATTRIBUTE_SYSTEM: {4} +.FILE_ATTRIBUTE_TEMPORARY: {256} +.FILE_ATTRIBUTE_VIRTUAL: {65536} diff --git a/crates/erg_compiler/ty/value.rs b/crates/erg_compiler/ty/value.rs index 7dabf5cf..e24da865 100644 --- a/crates/erg_compiler/ty/value.rs +++ b/crates/erg_compiler/ty/value.rs @@ -920,12 +920,29 @@ impl ValueObj { pub fn from_str(t: Type, mut content: Str) -> Option { match t { Type::Int => content.replace('_', "").parse::().ok().map(Self::Int), - Type::Nat => content - .trim_start_matches('-') // -0 -> 0 - .replace('_', "") - .parse::() - .ok() - .map(Self::Nat), + Type::Nat => { + let content = content + .trim_start_matches('-') // -0 -> 0 + .replace('_', ""); + if content.len() <= 1 { + return content.parse::().ok().map(Self::Nat); + } + match &content[0..=1] { + pre @ ("0b" | "0B") => { + let content = content.trim_start_matches(pre); + u64::from_str_radix(content, 2).ok().map(Self::Nat) + } + pre @ ("0o" | "0O") => { + let content = content.trim_start_matches(pre); + u64::from_str_radix(content, 8).ok().map(Self::Nat) + } + pre @ ("0x" | "0X") => { + let content = content.trim_start_matches(pre); + u64::from_str_radix(content, 16).ok().map(Self::Nat) + } + _ => content.parse::().ok().map(Self::Nat), + } + } Type::Float => content .replace('_', "") .parse::() diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index cca2a9ff..210da660 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -2569,8 +2569,10 @@ impl Locational for TupleTypeSpec { fn loc(&self) -> Location { if let Some((lparen, rparen)) = &self.parens { Location::concat(lparen, rparen) - } else { + } else if !self.tys.is_empty() { Location::concat(self.tys.first().unwrap(), self.tys.last().unwrap()) + } else { + Location::Unknown } } } @@ -2701,8 +2703,20 @@ impl Locational for TypeSpec { Self::Array(arr) => arr.loc(), Self::SetWithLen(set) => set.loc(), Self::Tuple(tup) => tup.loc(), - Self::Dict(dict) => Location::concat(&dict.first().unwrap().0, &dict.last().unwrap().1), - Self::Record(rec) => Location::concat(&rec.first().unwrap().0, &rec.last().unwrap().1), + Self::Dict(dict) => { + if dict.is_empty() { + Location::Unknown + } else { + Location::concat(&dict.first().unwrap().0, &dict.last().unwrap().1) + } + } + Self::Record(rec) => { + if rec.is_empty() { + Location::Unknown + } else { + Location::concat(&rec.first().unwrap().0, &rec.last().unwrap().1) + } + } Self::Enum(set) => set.loc(), Self::Interval { lhs, rhs, .. } => Location::concat(lhs, rhs), Self::Subr(s) => s.loc(), @@ -2854,7 +2868,11 @@ impl_displayable_stream_for_wrapper!(TypeBoundSpecs, TypeBoundSpec); impl Locational for TypeBoundSpecs { fn loc(&self) -> Location { - Location::concat(self.first().unwrap(), self.last().unwrap()) + if self.0.is_empty() { + Location::Unknown + } else { + Location::concat(self.first().unwrap(), self.last().unwrap()) + } } } @@ -3003,7 +3021,11 @@ impl NestedDisplay for Namespaces { impl Locational for Namespaces { fn loc(&self) -> Location { - Location::concat(self.first().unwrap(), self.last().unwrap()) + if self.0.is_empty() { + Location::Unknown + } else { + Location::concat(self.first().unwrap(), self.last().unwrap()) + } } } @@ -3252,7 +3274,7 @@ impl Locational for VarTuplePattern { fn loc(&self) -> Location { match &self.paren { Some((l, r)) => Location::concat(l, r), - None => Location::concat(&self.elems[0], self.elems.last().unwrap()), + None => self.elems.loc(), } } } @@ -3506,6 +3528,16 @@ impl NestedDisplay for Vars { impl_display_from_nested!(Vars); impl_stream!(Vars, VarSignature, elems); +impl Locational for Vars { + fn loc(&self) -> Location { + if self.elems.is_empty() { + Location::Unknown + } else { + Location::concat(self.first().unwrap(), self.last().unwrap()) + } + } +} + impl Vars { pub const fn new(elems: Vec) -> Self { Self { elems } diff --git a/crates/erg_parser/lex.rs b/crates/erg_parser/lex.rs index 8d2afcf1..31b74df6 100644 --- a/crates/erg_parser/lex.rs +++ b/crates/erg_parser/lex.rs @@ -621,6 +621,18 @@ impl Lexer /*<'a>*/ { n if n.is_ascii_digit() || n == '_' => { num.push(self.consume().unwrap()); } + 'b' | 'B' => { + num.push(self.consume().unwrap()); + return self.lex_bin(num); + } + 'o' | 'O' => { + num.push(self.consume().unwrap()); + return self.lex_oct(num); + } + 'x' | 'X' => { + num.push(self.consume().unwrap()); + return self.lex_hex(num); + } c if Self::is_valid_continue_symbol_ch(c) => { // exponent (e.g. 10e+3) if c == 'e' @@ -682,6 +694,39 @@ impl Lexer /*<'a>*/ { } } + fn lex_bin(&mut self, mut num: String) -> LexResult { + while let Some(cur) = self.peek_cur_ch() { + if cur == '0' || cur == '1' || cur == '_' { + num.push(self.consume().unwrap()); + } else { + break; + } + } + Ok(self.emit_token(BinLit, &num)) + } + + fn lex_oct(&mut self, mut num: String) -> LexResult { + while let Some(cur) = self.peek_cur_ch() { + if matches!(cur, '0'..='7') || cur == '_' { + num.push(self.consume().unwrap()); + } else { + break; + } + } + Ok(self.emit_token(OctLit, &num)) + } + + fn lex_hex(&mut self, mut num: String) -> LexResult { + while let Some(cur) = self.peek_cur_ch() { + if cur.is_ascii_hexdigit() || cur == '_' { + num.push(self.consume().unwrap()); + } else { + break; + } + } + Ok(self.emit_token(HexLit, &num)) + } + /// int_part_and_point must be like `12.` fn lex_ratio(&mut self, intpart_and_point: String) -> LexResult { let mut num = intpart_and_point; @@ -1547,7 +1592,7 @@ impl Iterator for Lexer /*<'a>*/ { None, ))) } - // IntLit or RatioLit + // IntLit (or Bin/Oct/Hex) or RatioLit Some(n) if n.is_ascii_digit() => Some(self.lex_num(n)), // Symbol (includes '_') Some(c) if Self::is_valid_start_symbol_ch(c) => Some(self.lex_symbol(c)), diff --git a/crates/erg_parser/token.rs b/crates/erg_parser/token.rs index 811835ed..7ba76d3f 100644 --- a/crates/erg_parser/token.rs +++ b/crates/erg_parser/token.rs @@ -24,6 +24,12 @@ pub enum TokenKind { NatLit, /// e.g. -1, -2 IntLit, + /// e.g. 0b101 + BinLit, + /// e.g. 0o777 + OctLit, + /// e.g. 0xdeadbeef + HexLit, RatioLit, BoolLit, StrLit, @@ -232,8 +238,8 @@ impl TokenKind { pub const fn category(&self) -> TokenCategory { match self { Symbol => TokenCategory::Symbol, - NatLit | IntLit | RatioLit | StrLit | BoolLit | NoneLit | EllipsisLit | InfLit - | DocComment => TokenCategory::Literal, + NatLit | BinLit | OctLit | HexLit | IntLit | RatioLit | StrLit | BoolLit | NoneLit + | EllipsisLit | InfLit | DocComment => TokenCategory::Literal, StrInterpLeft => TokenCategory::StrInterpLeft, StrInterpMid => TokenCategory::StrInterpMid, StrInterpRight => TokenCategory::StrInterpRight, diff --git a/tests/should_ok/decimal.er b/tests/should_ok/decimal.er new file mode 100644 index 00000000..3a6ae2ab --- /dev/null +++ b/tests/should_ok/decimal.er @@ -0,0 +1,8 @@ +b = 0b010010 +assert b == 18 + +o = 0o22 +assert o == 18 + +h = 0x12 +assert h == 18 diff --git a/tests/test.rs b/tests/test.rs index d10cb0f8..8ec0d606 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -52,6 +52,11 @@ fn exec_control_expr() -> Result<(), ()> { expect_success("tests/should_ok/control_expr.er", 3) } +#[test] +fn exec_decimal() -> Result<(), ()> { + expect_success("tests/should_ok/decimal.er", 0) +} + #[test] fn exec_default_param() -> Result<(), ()> { expect_success("tests/should_ok/default_param.er", 0)