diff --git a/Cargo.lock b/Cargo.lock index f3ea4ba4d..aa4612c77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -822,6 +822,8 @@ dependencies = [ "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1092,6 +1094,42 @@ name = "ucd-util" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-normalization" version = "0.1.8" @@ -1419,6 +1457,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum unic-char-property 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +"checksum unic-char-range 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" +"checksum unic-common 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +"checksum unic-emoji-char 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +"checksum unic-ucd-version 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index e57d42140..f2f8e5a6b 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -14,4 +14,5 @@ log="0.4.1" regex="0.2.2" num-bigint = "0.2" num-traits = "0.2" - +unicode-xid = "0.1.0" +unic-emoji-char = "0.9.0" diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 113f7afb1..defa5e232 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -1,12 +1,17 @@ //! This module takes care of lexing python source text. This means source //! code is translated into separate tokens. +extern crate unic_emoji_char; +extern crate unicode_xid; + pub use super::token::Tok; use num_bigint::BigInt; use num_traits::Num; use std::cmp::Ordering; use std::collections::HashMap; use std::str::FromStr; +use unic_emoji_char::is_emoji_presentation; +use unicode_xid::UnicodeXID; #[derive(Clone, Copy, PartialEq, Debug)] struct IndentationLevel { @@ -300,7 +305,7 @@ where } } - while self.is_char() { + while self.is_identifier_continuation() { name.push(self.next_char().unwrap()); } let end_pos = self.get_pos(); @@ -540,10 +545,21 @@ where Ok((start_pos, tok, end_pos)) } - fn is_char(&self) -> bool { - match self.chr0 { - Some('a'..='z') | Some('A'..='Z') | Some('_') | Some('0'..='9') => true, - _ => false, + fn is_identifier_start(&self, c: char) -> bool { + match c { + '_' => true, + c => UnicodeXID::is_xid_start(c), + } + } + + fn is_identifier_continuation(&self) -> bool { + if let Some(c) = self.chr0 { + match c { + '_' | '0'..='9' => true, + c => UnicodeXID::is_xid_continue(c), + } + } else { + false } } @@ -686,347 +702,380 @@ where } } - match self.chr0 { - Some('0'..='9') => return Some(self.lex_number()), - Some('_') | Some('a'..='z') | Some('A'..='Z') => { + // Check if we have some character: + if let Some(c) = self.chr0 { + // First check identifier: + if self.is_identifier_start(c) { return Some(self.lex_identifier()); - } - Some('#') => { - self.lex_comment(); - continue; - } - Some('"') => { - return Some(self.lex_string(false, false, false, false)); - } - Some('\'') => { - return Some(self.lex_string(false, false, false, false)); - } - Some('=') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::EqEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Equal, tok_end))); - } - } - } - Some('+') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Plus, tok_end))); - } - } - Some('*') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::StarEqual, tok_end))); - } - Some('*') => { - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleStarEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); - } - } - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Star, tok_end))); - } - } - } - Some('/') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::SlashEqual, tok_end))); - } - Some('/') => { - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleSlashEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); - } - } - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Slash, tok_end))); - } - } - } - Some('%') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Percent, tok_end))); - } - } - Some('|') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Vbar, tok_end))); - } - } - Some('^') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); - } - } - Some('&') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Amper, tok_end))); - } - } - Some('-') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::MinusEqual, tok_end))); - } - Some('>') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Rarrow, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Minus, tok_end))); - } - } - } - Some('@') => { - let tok_start = self.get_pos(); - self.next_char(); - if let Some('=') = self.chr0 { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::AtEqual, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::At, tok_end))); - } - } - Some('!') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::NotEqual, tok_end))); - } - _ => panic!("Invalid token '!'"), - } - } - Some('~') => { - return Some(self.eat_single_char(Tok::Tilde)); - } - Some('(') => { - let result = self.eat_single_char(Tok::Lpar); - self.nesting += 1; - return Some(result); - } - Some(')') => { - let result = self.eat_single_char(Tok::Rpar); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('[') => { - let result = self.eat_single_char(Tok::Lsqb); - self.nesting += 1; - return Some(result); - } - Some(']') => { - let result = self.eat_single_char(Tok::Rsqb); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some('{') => { - let result = self.eat_single_char(Tok::Lbrace); - self.nesting += 1; - return Some(result); - } - Some('}') => { - let result = self.eat_single_char(Tok::Rbrace); - if self.nesting == 0 { - return Some(Err(LexicalError::NestingError)); - } - self.nesting -= 1; - return Some(result); - } - Some(':') => { - return Some(self.eat_single_char(Tok::Colon)); - } - Some(';') => { - return Some(self.eat_single_char(Tok::Semi)); - } - Some('<') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('<') => { - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LeftShiftEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LeftShift, tok_end))); - } - } - } - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::LessEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Less, tok_end))); - } - } - } - Some('>') => { - let tok_start = self.get_pos(); - self.next_char(); - match self.chr0 { - Some('>') => { - self.next_char(); - match self.chr0 { - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShiftEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::RightShift, tok_end))); - } - } - } - Some('=') => { - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); - } - _ => { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Greater, tok_end))); - } - } - } - Some(',') => { + } else if is_emoji_presentation(c) { let tok_start = self.get_pos(); self.next_char(); let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Comma, tok_end))); - } - Some('.') => { - let tok_start = self.get_pos(); - self.next_char(); - if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { - self.next_char(); - self.next_char(); - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); - } else { - let tok_end = self.get_pos(); - return Some(Ok((tok_start, Tok::Dot, tok_end))); - } - } - Some('\n') => { - let tok_start = self.get_pos(); - self.next_char(); - let tok_end = self.get_pos(); - self.new_line(); + println!("Emoji: {}", c); + return Some(Ok(( + tok_start, + Tok::Name { + name: c.to_string(), + }, + tok_end, + ))); + } else { + match c { + '0'..='9' => return Some(self.lex_number()), + '#' => { + self.lex_comment(); + continue; + } + '"' => { + return Some(self.lex_string(false, false, false, false)); + } + '\'' => { + return Some(self.lex_string(false, false, false, false)); + } + '=' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::EqEqual, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Equal, tok_end))); + } + } + } + '+' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PlusEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Plus, tok_end))); + } + } + '*' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::StarEqual, tok_end))); + } + Some('*') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleStarEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::DoubleStar, tok_end))); + } + } + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Star, tok_end))); + } + } + } + '/' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::SlashEqual, tok_end))); + } + Some('/') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::DoubleSlashEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::DoubleSlash, tok_end))); + } + } + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Slash, tok_end))); + } + } + } + '%' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::PercentEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Percent, tok_end))); + } + } + '|' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::VbarEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Vbar, tok_end))); + } + } + '^' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumflexEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::CircumFlex, tok_end))); + } + } + '&' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AmperEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Amper, tok_end))); + } + } + '-' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::MinusEqual, tok_end))); + } + Some('>') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Rarrow, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Minus, tok_end))); + } + } + } + '@' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::AtEqual, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::At, tok_end))); + } + } + '!' => { + let tok_start = self.get_pos(); + self.next_char(); + if let Some('=') = self.chr0 { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::NotEqual, tok_end))); + } else { + return Some(Err(LexicalError::UnrecognizedToken { tok: '!' })); + } + } + '~' => { + return Some(self.eat_single_char(Tok::Tilde)); + } + '(' => { + let result = self.eat_single_char(Tok::Lpar); + self.nesting += 1; + return Some(result); + } + ')' => { + let result = self.eat_single_char(Tok::Rpar); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '[' => { + let result = self.eat_single_char(Tok::Lsqb); + self.nesting += 1; + return Some(result); + } + ']' => { + let result = self.eat_single_char(Tok::Rsqb); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + '{' => { + let result = self.eat_single_char(Tok::Lbrace); + self.nesting += 1; + return Some(result); + } + '}' => { + let result = self.eat_single_char(Tok::Rbrace); + if self.nesting == 0 { + return Some(Err(LexicalError::NestingError)); + } + self.nesting -= 1; + return Some(result); + } + ':' => { + return Some(self.eat_single_char(Tok::Colon)); + } + ';' => { + return Some(self.eat_single_char(Tok::Semi)); + } + '<' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('<') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::LeftShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::LeftShift, tok_end))); + } + } + } + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::LessEqual, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Less, tok_end))); + } + } + } + '>' => { + let tok_start = self.get_pos(); + self.next_char(); + match self.chr0 { + Some('>') => { + self.next_char(); + match self.chr0 { + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok(( + tok_start, + Tok::RightShiftEqual, + tok_end, + ))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::RightShift, tok_end))); + } + } + } + Some('=') => { + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::GreaterEqual, tok_end))); + } + _ => { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Greater, tok_end))); + } + } + } + ',' => { + let tok_start = self.get_pos(); + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Comma, tok_end))); + } + '.' => { + let tok_start = self.get_pos(); + self.next_char(); + if let (Some('.'), Some('.')) = (&self.chr0, &self.chr1) { + self.next_char(); + self.next_char(); + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Ellipsis, tok_end))); + } else { + let tok_end = self.get_pos(); + return Some(Ok((tok_start, Tok::Dot, tok_end))); + } + } + '\n' => { + let tok_start = self.get_pos(); + self.next_char(); + let tok_end = self.get_pos(); + self.new_line(); - // Depending on the nesting level, we emit newline or not: - if self.nesting == 0 { - self.at_begin_of_line = true; - return Some(Ok((tok_start, Tok::Newline, tok_end))); - } else { - continue; + // Depending on the nesting level, we emit newline or not: + if self.nesting == 0 { + self.at_begin_of_line = true; + return Some(Ok((tok_start, Tok::Newline, tok_end))); + } else { + continue; + } + } + ' ' => { + // Skip whitespaces + self.next_char(); + continue; + } + _ => { + let c = self.next_char(); + return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); + } // Ignore all the rest.. } } - Some(' ') => { - // Skip whitespaces - self.next_char(); - continue; - } - None => return None, - _ => { - let c = self.next_char(); - return Some(Err(LexicalError::UnrecognizedToken { tok: c.unwrap() })); - } // Ignore all the rest.. + } else { + return None; } } } diff --git a/src/main.rs b/src/main.rs index 70adf1008..750faf98a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -153,7 +153,7 @@ fn get_prompt(vm: &VirtualMachine, prompt_name: &str) -> String { fn run_shell(vm: &VirtualMachine) -> PyResult { println!( - "Welcome to the magnificent Rust Python {} interpreter", + "Welcome to the magnificent Rust Python {} interpreter \u{1f631} \u{1f596}", crate_version!() ); let vars = vm.ctx.new_scope(); diff --git a/tests/.travis-runner.sh b/tests/.travis-runner.sh index 8a004d3b1..36b9f43ac 100755 --- a/tests/.travis-runner.sh +++ b/tests/.travis-runner.sh @@ -31,7 +31,7 @@ then zip -0 ccov.zip `find . \( -name "rustpython*.gc*" \) -print` # Install grcov - curl -L https://github.com/mozilla/grcov/releases/download/v0.4.1/grcov-linux-x86_64.tar.bz2 | tar jxf - + curl -L https://github.com/mozilla/grcov/releases/download/v0.4.2/grcov-linux-x86_64.tar.bz2 | tar jxf - ./grcov ccov.zip -s . -t lcov --llvm --branch --ignore-not-existing --ignore-dir "/*" -p "x" > lcov.info diff --git a/tests/snippets/append.py b/tests/snippets/append.py deleted file mode 100644 index a0490cb6d..000000000 --- a/tests/snippets/append.py +++ /dev/null @@ -1,2 +0,0 @@ -x = [] -x.append(1) diff --git a/tests/snippets/builtin_range.py b/tests/snippets/builtin_range.py index 89014880c..2ea71f7a7 100644 --- a/tests/snippets/builtin_range.py +++ b/tests/snippets/builtin_range.py @@ -50,3 +50,7 @@ assert 'foo' not in range(10) assert list(reversed(range(5))) == [4, 3, 2, 1, 0] assert list(reversed(range(5, 0, -1))) == [1, 2, 3, 4, 5] assert list(reversed(range(1,10,5))) == [6, 1] + +# range retains the original int refs +i = 2**64 +assert range(i).stop is i diff --git a/tests/snippets/set.py b/tests/snippets/set.py index a7a20b0ef..9e49dff89 100644 --- a/tests/snippets/set.py +++ b/tests/snippets/set.py @@ -144,3 +144,88 @@ a ^= set([3,4,5]) assert a == set([1,2,4,5]) with assertRaises(TypeError): a ^= 1 + +# frozen set + +assert frozenset([1,2]) == frozenset([1,2]) +assert not frozenset([1,2,3]) == frozenset([1,2]) + +assert frozenset([1,2,3]) >= frozenset([1,2]) +assert frozenset([1,2]) >= frozenset([1,2]) +assert not frozenset([1,3]) >= frozenset([1,2]) + +assert frozenset([1,2,3]).issuperset(frozenset([1,2])) +assert frozenset([1,2]).issuperset(frozenset([1,2])) +assert not frozenset([1,3]).issuperset(frozenset([1,2])) + +assert frozenset([1,2,3]) > frozenset([1,2]) +assert not frozenset([1,2]) > frozenset([1,2]) +assert not frozenset([1,3]) > frozenset([1,2]) + +assert frozenset([1,2]) <= frozenset([1,2,3]) +assert frozenset([1,2]) <= frozenset([1,2]) +assert not frozenset([1,3]) <= frozenset([1,2]) + +assert frozenset([1,2]).issubset(frozenset([1,2,3])) +assert frozenset([1,2]).issubset(frozenset([1,2])) +assert not frozenset([1,3]).issubset(frozenset([1,2])) + +assert frozenset([1,2]) < frozenset([1,2,3]) +assert not frozenset([1,2]) < frozenset([1,2]) +assert not frozenset([1,3]) < frozenset([1,2]) + +a = frozenset([1, 2, 3]) +assert len(a) == 3 +b = a.copy() +assert b == a + +assert frozenset([1,2,3]).union(frozenset([4,5])) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]).union(frozenset([1,2,3,4,5])) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]) | frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) | frozenset([1,2,3,4,5]) == frozenset([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(frozenset([1,2])) == frozenset([1,2]) +assert frozenset([1,2,3]).intersection(frozenset([5,6])) == frozenset([]) + +assert frozenset([1,2,3]) & frozenset([4,5]) == frozenset([]) +assert frozenset([1,2,3]) & frozenset([1,2,3,4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).difference(frozenset([5,6])) == frozenset([1,2,3]) + +assert frozenset([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) +assert frozenset([1,2,3]) - frozenset([1,2,3,4,5]) == frozenset([]) + +assert frozenset([1,2,3]).symmetric_difference(frozenset([1,2])) == frozenset([3]) +assert frozenset([1,2,3]).symmetric_difference(frozenset([5,6])) == frozenset([1,2,3,5,6]) + +assert frozenset([1,2,3]) ^ frozenset([4,5]) == frozenset([1,2,3,4,5]) +assert frozenset([1,2,3]) ^ frozenset([1,2,3,4,5]) == frozenset([4,5]) + +assert_raises(TypeError, lambda: frozenset([[]])) + +# set and frozen set +assert frozenset([1,2,3]).union(set([4,5])) == frozenset([1,2,3,4,5]) +assert set([1,2,3]).union(frozenset([4,5])) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]) | set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) | frozenset([4,5]) == set([1,2,3,4,5]) + +assert frozenset([1,2,3]).intersection(set([5,6])) == frozenset([]) +assert set([1,2,3]).intersection(frozenset([5,6])) == set([]) + +assert frozenset([1,2,3]) & set([1,2,3,4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) & frozenset([1,2,3,4,5]) == set([1,2,3]) + +assert frozenset([1,2,3]).difference(set([5,6])) == frozenset([1,2,3]) +assert set([1,2,3]).difference(frozenset([5,6])) == set([1,2,3]) + +assert frozenset([1,2,3]) - set([4,5]) == frozenset([1,2,3]) +assert set([1,2,3]) - frozenset([4,5]) == frozenset([1,2,3]) + +assert frozenset([1,2,3]).symmetric_difference(set([1,2])) == frozenset([3]) +assert set([1,2,3]).symmetric_difference(frozenset([1,2])) == set([3]) + +assert frozenset([1,2,3]) ^ set([4,5]) == frozenset([1,2,3,4,5]) +assert set([1,2,3]) ^ frozenset([4,5]) == set([1,2,3,4,5]) diff --git a/tests/snippets/strings.py b/tests/snippets/strings.py index 4483b37f6..31511db11 100644 --- a/tests/snippets/strings.py +++ b/tests/snippets/strings.py @@ -1,3 +1,5 @@ +from testutils import assert_raises + assert "a" == 'a' assert """a""" == "a" assert len(""" " "" " "" """) == 11 @@ -124,3 +126,9 @@ assert 'a' <= 'a' assert 'z' > 'b' assert 'z' >= 'b' assert 'a' >= 'a' + +def try_mutate_str(): + word = "word" + word[0] = 'x' + +assert_raises(TypeError, try_mutate_str) diff --git a/tests/snippets/unicode_fu.py b/tests/snippets/unicode_fu.py new file mode 100644 index 000000000..96d5bf977 --- /dev/null +++ b/tests/snippets/unicode_fu.py @@ -0,0 +1,13 @@ + +# Test the unicode support! 👋 + + +áš´=2 + +assert áš´*8 == 16 + +áš´="👋" + +c = áš´*3 + +assert c == '👋👋👋' diff --git a/tests/snippets/xfail_3.1.2.17.py b/tests/snippets/xfail_3.1.2.17.py deleted file mode 100644 index fc956bef4..000000000 --- a/tests/snippets/xfail_3.1.2.17.py +++ /dev/null @@ -1,7 +0,0 @@ -word = "Python" - -word[0] = "J" # Should raise a error, immutable -word[2:] = "Jy" # Should raise a error, immutable - - - diff --git a/tests/test_snippets.py b/tests/test_snippets.py index 4be73b7bd..7edbd7b7e 100644 --- a/tests/test_snippets.py +++ b/tests/test_snippets.py @@ -17,7 +17,6 @@ import compile_code class _TestType(enum.Enum): functional = 1 - benchmark = 2 logger = logging.getLogger('tests') @@ -25,7 +24,6 @@ ROOT_DIR = '..' TEST_ROOT = os.path.abspath(os.path.join(ROOT_DIR, 'tests')) TEST_DIRS = { _TestType.functional: os.path.join(TEST_ROOT, 'snippets'), - _TestType.benchmark: os.path.join(TEST_ROOT, 'benchmarks'), } CPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR, 'py_code_object')) RUSTPYTHON_RUNNER_DIR = os.path.abspath(os.path.join(ROOT_DIR)) @@ -65,8 +63,7 @@ def run_via_cpython_bytecode(filename, test_type): # Step2: run cpython bytecode: env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'debug' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' with pushd(CPYTHON_RUNNER_DIR): subprocess.check_call(['cargo', 'run', bytecode_filename], env=env) @@ -74,15 +71,15 @@ def run_via_cpython_bytecode(filename, test_type): def run_via_rustpython(filename, test_type): env = os.environ.copy() - log_level = 'info' if test_type == _TestType.benchmark else 'trace' - env['RUST_LOG'] = '{},cargo=error,jobserver=error'.format(log_level) + env['RUST_LOG'] = 'info,cargo=error,jobserver=error' env['RUST_BACKTRACE'] = '1' + + target = 'release' if env.get('CODE_COVERAGE', 'false') == 'true': - subprocess.check_call( - ['cargo', 'run', filename], env=env) - else: - subprocess.check_call( - ['cargo', 'run', '--release', filename], env=env) + target = 'debug' + binary = os.path.abspath(os.path.join(ROOT_DIR, 'target', target, 'rustpython')) + + subprocess.check_call([binary, filename], env=env) def create_test_function(cls, filename, method, test_type): @@ -124,4 +121,7 @@ def get_test_files(): # @populate('cpython_bytecode') @populate('rustpython') class SampleTestCase(unittest.TestCase): - pass + @classmethod + def setUpClass(cls): + subprocess.check_call(['cargo', 'build']) + subprocess.check_call(['cargo', 'build', '--release']) diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 9dfe7afd6..4d9fb7989 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -11,7 +11,6 @@ use num_traits::{Signed, ToPrimitive}; use crate::compile; use crate::import::import_module; use crate::obj::objbool; -use crate::obj::objdict; use crate::obj::objint; use crate::obj::objiter; use crate::obj::objstr::{self, PyStringRef}; @@ -29,14 +28,7 @@ use crate::obj::objcode::PyCodeRef; use crate::stdlib::io::io_open; fn get_locals(vm: &VirtualMachine) -> PyObjectRef { - let d = vm.new_dict(); - // TODO: implement dict_iter_items? - let locals = vm.get_locals(); - let key_value_pairs = objdict::get_key_value_pairs(&locals); - for (key, value) in key_value_pairs { - objdict::set_item(&d, vm, &key, &value); - } - d + vm.get_locals() } fn dir_locals(vm: &VirtualMachine) -> PyObjectRef { @@ -823,9 +815,9 @@ pub fn builtin_build_class_(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResu let prepare = vm.get_attribute(metaclass.clone().into_object(), "__prepare__")?; let namespace = vm.invoke(prepare, vec![name_arg.clone(), bases.clone()])?; - let cells = vm.new_dict(); + let cells = vm.ctx.new_dict(); - vm.invoke_with_locals(function, cells.clone(), namespace.clone())?; + vm.invoke_with_locals(function, cells.clone().into_object(), namespace.clone())?; let class = vm.call_method( metaclass.as_object(), "__call__", diff --git a/vm/src/frame.rs b/vm/src/frame.rs index 2a0951ab3..3584a57a8 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -119,7 +119,7 @@ impl Scope { } pub fn child_scope(&self, ctx: &PyContext) -> Scope { - self.child_scope_with_locals(ctx.new_dict()) + self.child_scope_with_locals(ctx.new_dict().into_object()) } } @@ -142,7 +142,7 @@ impl NameProtocol for Scope { return Some(value); } - vm.builtins.get_item(name) + vm.get_attribute(vm.builtins.clone(), name).ok() } fn load_cell(&self, _vm: &VirtualMachine, name: &str) -> Option { @@ -386,7 +386,7 @@ impl Frame { Ok(None) } bytecode::Instruction::BuildMap { size, unpack } => { - let map_obj = vm.ctx.new_dict(); + let map_obj = vm.ctx.new_dict().into_object(); for _x in 0..*size { let obj = self.pop_value(); if *unpack { @@ -413,7 +413,7 @@ impl Frame { if x.is(&vm.ctx.none()) { None } else if let Some(i) = x.payload::() { - Some(i.value.clone()) + Some(i.as_bigint().clone()) } else { panic!("Expect Int or None as BUILD_SLICE arguments") } @@ -572,7 +572,7 @@ impl Frame { let annotations = if flags.contains(bytecode::FunctionOpArg::HAS_ANNOTATIONS) { self.pop_value() } else { - vm.new_dict() + vm.ctx.new_dict().into_object() }; let defaults = if flags.contains(bytecode::FunctionOpArg::HAS_DEFAULTS) { @@ -839,8 +839,10 @@ impl Frame { let module = vm.import(module)?; // Grab all the names from the module and put them in the context - for (k, v) in module.get_key_value_pairs().iter() { - self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + if let Some(dict) = &module.dict { + for (k, v) in dict.get_key_value_pairs().iter() { + self.scope.store_name(&vm, &objstr::get_value(k), v.clone()); + } } Ok(None) } diff --git a/vm/src/function.rs b/vm/src/function.rs index 1e2a38f71..ed959d85c 100644 --- a/vm/src/function.rs +++ b/vm/src/function.rs @@ -247,6 +247,15 @@ where } } +impl IntoIterator for KwArgs { + type Item = (String, T); + type IntoIter = std::collections::hash_map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + /// A list of positional argument values. /// /// A built-in function with a `Args` parameter is analagous to a Python diff --git a/vm/src/import.rs b/vm/src/import.rs index 936b89680..987555915 100644 --- a/vm/src/import.rs +++ b/vm/src/import.rs @@ -40,7 +40,7 @@ fn import_uncached_module(vm: &VirtualMachine, current_path: PathBuf, module: &s let attrs = vm.ctx.new_dict(); attrs.set_item(&vm.ctx, "__name__", vm.new_str(module.to_string())); - vm.run_code_obj(code_obj, Scope::new(None, attrs.clone()))?; + vm.run_code_obj(code_obj, Scope::new(None, attrs.clone().into_object()))?; Ok(vm.ctx.new_module(module, attrs)) } diff --git a/vm/src/obj/objbool.rs b/vm/src/obj/objbool.rs index e21a3a329..d4ef4b0fb 100644 --- a/vm/src/obj/objbool.rs +++ b/vm/src/obj/objbool.rs @@ -25,7 +25,7 @@ pub fn boolval(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { Ok(if let Ok(f) = vm.get_method(obj.clone(), "__bool__") { let bool_res = vm.invoke(f, PyFuncArgs::default())?; match bool_res.payload::() { - Some(i) => !i.value.is_zero(), + Some(i) => !i.as_bigint().is_zero(), None => return Err(vm.new_type_error(String::from("TypeError"))), } } else { @@ -59,7 +59,7 @@ pub fn not(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult { // Retrieve inner int value: pub fn get_value(obj: &PyObjectRef) -> bool { - !obj.payload::().unwrap().value.is_zero() + !obj.payload::().unwrap().as_bigint().is_zero() } fn bool_repr(vm: &VirtualMachine, args: PyFuncArgs) -> Result { diff --git a/vm/src/obj/objdict.rs b/vm/src/obj/objdict.rs index 0c8d5fe5e..061c3477e 100644 --- a/vm/src/obj/objdict.rs +++ b/vm/src/obj/objdict.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use std::fmt; use std::ops::{Deref, DerefMut}; -use crate::function::{OptionalArg, PyFuncArgs}; +use crate::function::{KwArgs, OptionalArg}; use crate::pyobject::{ - PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + DictProtocol, PyAttributes, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, }; use crate::vm::{ReprGuard, VirtualMachine}; @@ -124,57 +124,48 @@ pub fn py_dict_to_attributes(dict: &PyObjectRef) -> PyAttributes { attrs } -pub fn attributes_to_py_dict(vm: &VirtualMachine, attributes: PyAttributes) -> PyResult { - let dict = vm.ctx.new_dict(); - for (key, value) in attributes { - let key = vm.ctx.new_str(key); - set_item(&dict, vm, &key, &value); - } - Ok(dict) -} - // Python dict methods: -fn dict_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(_ty, Some(vm.ctx.type_type()))], - optional = [(dict_obj, None)] - ); - let dict = vm.ctx.new_dict(); - if let Some(dict_obj) = dict_obj { - if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { - for (needle, value) in get_key_value_pairs(&dict_obj) { - set_item(&dict, vm, &needle, &value); - } - } else { - let iter = objiter::get_iter(vm, dict_obj)?; - loop { - fn err(vm: &VirtualMachine) -> PyObjectRef { - vm.new_type_error("Iterator must have exactly two elements".to_string()) +impl PyDictRef { + fn new( + _class: PyClassRef, // TODO Support subclasses of int. + dict_obj: OptionalArg, + kwargs: KwArgs, + vm: &VirtualMachine, + ) -> PyResult { + let dict = vm.ctx.new_dict(); + if let OptionalArg::Present(dict_obj) = dict_obj { + if objtype::isinstance(&dict_obj, &vm.ctx.dict_type()) { + for (needle, value) in get_key_value_pairs(&dict_obj) { + set_item(dict.as_object(), vm, &needle, &value); } - let element = match objiter::get_next_object(vm, &iter)? { - Some(obj) => obj, - None => break, - }; - let elem_iter = objiter::get_iter(vm, &element)?; - let needle = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; - if objiter::get_next_object(vm, &elem_iter)?.is_some() { - return Err(err(vm)); + } else { + let iter = objiter::get_iter(vm, &dict_obj)?; + loop { + fn err(vm: &VirtualMachine) -> PyObjectRef { + vm.new_type_error("Iterator must have exactly two elements".to_string()) + } + let element = match objiter::get_next_object(vm, &iter)? { + Some(obj) => obj, + None => break, + }; + let elem_iter = objiter::get_iter(vm, &element)?; + let needle = + objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + let value = objiter::get_next_object(vm, &elem_iter)?.ok_or_else(|| err(vm))?; + if objiter::get_next_object(vm, &elem_iter)?.is_some() { + return Err(err(vm)); + } + set_item(dict.as_object(), vm, &needle, &value); } - set_item(&dict, vm, &needle, &value); } } + for (needle, value) in kwargs.into_iter() { + let py_needle = vm.new_str(needle); + set_item(&dict.as_object(), vm, &py_needle, &value); + } + Ok(dict) } - for (needle, value) in args.kwargs { - let py_needle = vm.new_str(needle); - set_item(&dict, vm, &py_needle, &value); - } - Ok(dict) -} -impl PyDictRef { fn bool(self, _vm: &VirtualMachine) -> bool { !self.entries.borrow().is_empty() } @@ -302,6 +293,31 @@ impl PyDictRef { } } +impl DictProtocol for PyDictRef { + fn contains_key(&self, k: &str) -> bool { + content_contains_key_str(&self.entries.borrow(), k) + } + + fn get_item(&self, k: &str) -> Option { + content_get_key_str(&self.entries.borrow(), k) + } + + fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { + get_key_value_pairs(self.as_object()) + } + + // Item set/get: + fn set_item(&self, ctx: &PyContext, key: &str, v: PyObjectRef) { + let key = ctx.new_str(key.to_string()); + set_item_in_content(&mut self.entries.borrow_mut(), &key, &v); + } + + fn del_item(&self, key: &str) { + let mut elements = get_mut_elements(self.as_object()); + elements.remove(key).unwrap(); + } +} + pub fn init(context: &PyContext) { extend_class!(context, &context.dict_type, { "__bool__" => context.new_rustfunc(PyDictRef::bool), @@ -310,7 +326,7 @@ pub fn init(context: &PyContext) { "__delitem__" => context.new_rustfunc(PyDictRef::delitem), "__getitem__" => context.new_rustfunc(PyDictRef::getitem), "__iter__" => context.new_rustfunc(PyDictRef::iter), - "__new__" => context.new_rustfunc(dict_new), + "__new__" => context.new_rustfunc(PyDictRef::new), "__repr__" => context.new_rustfunc(PyDictRef::repr), "__setitem__" => context.new_rustfunc(PyDictRef::setitem), "clear" => context.new_rustfunc(PyDictRef::clear), diff --git a/vm/src/obj/objenumerate.rs b/vm/src/obj/objenumerate.rs index 0b0e5d448..68d1e49f6 100644 --- a/vm/src/obj/objenumerate.rs +++ b/vm/src/obj/objenumerate.rs @@ -32,7 +32,7 @@ fn enumerate_new( vm: &VirtualMachine, ) -> PyResult { let counter = match start { - OptionalArg::Present(start) => start.value.clone(), + OptionalArg::Present(start) => start.as_bigint().clone(), OptionalArg::Missing => BigInt::zero(), }; diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 7c19968da..dd40f06a4 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::hash::{Hash, Hasher}; use num_bigint::{BigInt, ToBigInt}; @@ -18,8 +19,13 @@ use crate::obj::objtype::PyClassRef; #[derive(Debug)] pub struct PyInt { - // TODO: shouldn't be public - pub value: BigInt, + value: BigInt, +} + +impl fmt::Display for PyInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + BigInt::fmt(&self.value, f) + } } pub type PyIntRef = PyRef; @@ -28,6 +34,10 @@ impl PyInt { pub fn new>(i: T) -> Self { PyInt { value: i.into() } } + + pub fn as_bigint(&self) -> &BigInt { + &self.value + } } impl IntoPyObject for BigInt { diff --git a/vm/src/obj/objmodule.rs b/vm/src/obj/objmodule.rs index 58d8688db..b641f0426 100644 --- a/vm/src/obj/objmodule.rs +++ b/vm/src/obj/objmodule.rs @@ -1,12 +1,10 @@ -use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{DictProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{DictProtocol, PyContext, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; #[derive(Debug)] pub struct PyModule { pub name: String, - pub dict: PyObjectRef, } pub type PyModuleRef = PyRef; @@ -18,23 +16,21 @@ impl PyValue for PyModule { impl PyModuleRef { fn dir(self: PyModuleRef, vm: &VirtualMachine) -> PyResult { - let keys = self - .dict - .get_key_value_pairs() - .iter() - .map(|(k, _v)| k.clone()) - .collect(); - Ok(vm.ctx.new_list(keys)) - } - - fn set_attr(self, attr: PyStringRef, value: PyObjectRef, vm: &VirtualMachine) { - self.dict.set_item(&vm.ctx, &attr.value, value) + if let Some(dict) = &self.into_object().dict { + let keys = dict + .get_key_value_pairs() + .iter() + .map(|(k, _v)| k.clone()) + .collect(); + Ok(vm.ctx.new_list(keys)) + } else { + panic!("Modules should definitely have a dict."); + } } } pub fn init(context: &PyContext) { extend_class!(&context, &context.module_type, { "__dir__" => context.new_rustfunc(PyModuleRef::dir), - "__setattr__" => context.new_rustfunc(PyModuleRef::set_attr) }); } diff --git a/vm/src/obj/objobject.rs b/vm/src/obj/objobject.rs index 46a90114d..60473fad0 100644 --- a/vm/src/obj/objobject.rs +++ b/vm/src/obj/objobject.rs @@ -1,5 +1,5 @@ +use super::objdict::{self, PyDictRef}; use super::objlist::PyList; -use super::objmodule::PyModule; use super::objstr::{self, PyStringRef}; use super::objtype; use crate::function::PyFuncArgs; @@ -23,11 +23,12 @@ impl PyValue for PyInstance { pub fn new_instance(vm: &VirtualMachine, mut args: PyFuncArgs) -> PyResult { // more or less __new__ operator let cls = PyClassRef::try_from_object(vm, args.shift())?; - Ok(if cls.is(&vm.ctx.object) { - PyObject::new_without_dict(PyInstance, cls) + let dict = if cls.is(&vm.ctx.object) { + None } else { - PyObject::new(PyInstance, cls) - }) + Some(vm.ctx.new_dict()) + }; + Ok(PyObject::new(PyInstance, cls, dict)) } fn object_eq(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -114,7 +115,7 @@ fn object_setattr( } if let Some(ref dict) = obj.clone().dict { - dict.borrow_mut().insert(attr_name.value.clone(), value); + dict.set_item(&vm.ctx, &attr_name.value, value); Ok(()) } else { let type_name = objtype::get_type_name(obj.type_ref()); @@ -135,7 +136,7 @@ fn object_delattr(obj: PyObjectRef, attr_name: PyStringRef, vm: &VirtualMachine) } if let Some(ref dict) = obj.dict { - dict.borrow_mut().remove(&attr_name.value); + dict.del_item(&attr_name.value); Ok(()) } else { let type_name = objtype::get_type_name(obj.type_ref()); @@ -227,13 +228,9 @@ fn object_class_setter( Err(vm.new_type_error(format!("can't change class of type '{}'", type_repr))) } -fn object_dict(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - if let Some(ref dict) = args.args[0].dict { - let new_dict = vm.new_dict(); - for (attr, value) in dict.borrow().iter() { - new_dict.set_item(&vm.ctx, &attr, value.clone()); - } - Ok(new_dict) +fn object_dict(object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Some(ref dict) = object.dict { + Ok(dict.clone()) } else { Err(vm.new_type_error("TypeError: no dictionary.".to_string())) } @@ -273,15 +270,8 @@ fn object_getattribute(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } fn object_getattr(obj: &PyObjectRef, attr_name: &str) -> Option { - // TODO: - // This is an all kinds of wrong work-around for the temporary difference in - // shape between modules and object. It will disappear once that is fixed. - if let Some(PyModule { ref dict, .. }) = obj.payload::() { - return dict.get_item(attr_name); - } - if let Some(ref dict) = obj.dict { - dict.borrow().get(attr_name).cloned() + dict.get_item(attr_name) } else { None } @@ -293,8 +283,8 @@ pub fn get_attributes(obj: &PyObjectRef) -> PyAttributes { // Get instance attributes: if let Some(dict) = &obj.dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); + for (key, value) in objdict::get_key_value_pairs(dict.as_object()) { + attributes.insert(key.to_string(), value.clone()); } } diff --git a/vm/src/obj/objproperty.rs b/vm/src/obj/objproperty.rs index c5459a9fb..e6b11904e 100644 --- a/vm/src/obj/objproperty.rs +++ b/vm/src/obj/objproperty.rs @@ -200,7 +200,7 @@ impl<'a> PropertyBuilder<'a> { deleter: None, }; - PyObject::new(payload, self.ctx.property_type()) + PyObject::new(payload, self.ctx.property_type(), None) } else { let payload = PyReadOnlyProperty { getter: self.getter.expect( @@ -208,7 +208,7 @@ impl<'a> PropertyBuilder<'a> { ), }; - PyObject::new(payload, self.ctx.readonly_property_type()) + PyObject::new(payload, self.ctx.readonly_property_type(), None) } } } diff --git a/vm/src/obj/objrange.rs b/vm/src/obj/objrange.rs index 468490eea..04c9c273b 100644 --- a/vm/src/obj/objrange.rs +++ b/vm/src/obj/objrange.rs @@ -3,25 +3,21 @@ use std::ops::Mul; use num_bigint::{BigInt, Sign}; use num_integer::Integer; -use num_traits::{One, Signed, ToPrimitive, Zero}; +use num_traits::{One, Signed, Zero}; use crate::function::{OptionalArg, PyFuncArgs}; -use crate::pyobject::{ - Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, -}; +use crate::pyobject::{Either, PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue}; use crate::vm::VirtualMachine; -use super::objint::{self, PyInt, PyIntRef}; +use super::objint::{PyInt, PyIntRef}; use super::objslice::PySliceRef; -use super::objtype::{self, PyClassRef}; +use super::objtype::PyClassRef; #[derive(Debug, Clone)] pub struct PyRange { - // Unfortunately Rust's built in range type doesn't support things like indexing - // or ranges where start > end so we need to roll our own. - pub start: BigInt, - pub stop: BigInt, - pub step: BigInt, + pub start: PyIntRef, + pub stop: PyIntRef, + pub step: PyIntRef, } impl PyValue for PyRange { @@ -31,71 +27,38 @@ impl PyValue for PyRange { } impl PyRange { - #[inline] - pub fn try_len(&self) -> Option { - match self.step.sign() { - Sign::Plus if self.start < self.stop => ((&self.stop - &self.start - 1usize) - / &self.step) - .to_usize() - .map(|sz| sz + 1), - Sign::Minus if self.start > self.stop => ((&self.start - &self.stop - 1usize) - / (-&self.step)) - .to_usize() - .map(|sz| sz + 1), - _ => Some(0), - } - } - - #[inline] - pub fn len(&self) -> usize { - self.try_len().unwrap() - } - #[inline] fn offset(&self, value: &BigInt) -> Option { - match self.step.sign() { - Sign::Plus if *value >= self.start && *value < self.stop => Some(value - &self.start), - Sign::Minus if *value <= self.start && *value > self.stop => Some(&self.start - value), + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + match step.sign() { + Sign::Plus if value >= start && value < stop => Some(value - start), + Sign::Minus if value <= self.start.as_bigint() && value > stop => Some(start - value), _ => None, } } - #[inline] - pub fn contains(&self, value: &BigInt) -> bool { - match self.offset(value) { - Some(ref offset) => offset.is_multiple_of(&self.step), - None => false, - } - } - #[inline] pub fn index_of(&self, value: &BigInt) -> Option { + let step = self.step.as_bigint(); match self.offset(value) { - Some(ref offset) if offset.is_multiple_of(&self.step) => { - Some((offset / &self.step).abs()) - } + Some(ref offset) if offset.is_multiple_of(step) => Some((offset / step).abs()), Some(_) | None => None, } } - #[inline] - pub fn count(&self, value: &BigInt) -> usize { - if self.index_of(value).is_some() { - 1 - } else { - 0 - } - } - #[inline] pub fn is_empty(&self) -> bool { - (self.start <= self.stop && self.step.is_negative()) - || (self.start >= self.stop && self.step.is_positive()) + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + (start <= stop && step.is_negative()) || (start >= stop && step.is_positive()) } #[inline] pub fn forward(&self) -> bool { - self.start < self.stop + self.start.as_bigint() < self.stop.as_bigint() } #[inline] @@ -103,50 +66,20 @@ impl PyRange { where &'a BigInt: Mul, { - let result = &self.start + &self.step * index; + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); - if (self.forward() && !self.is_empty() && result < self.stop) - || (!self.forward() && !self.is_empty() && result > self.stop) + let result = start + step * index; + + if (self.forward() && !self.is_empty() && &result < stop) + || (!self.forward() && !self.is_empty() && &result > stop) { Some(result) } else { None } } - - #[inline] - pub fn reversed(&self) -> Self { - // compute the last element that is actually contained within the range - // this is the new start - let remainder = ((&self.stop - &self.start) % &self.step).abs(); - let start = if remainder.is_zero() { - &self.stop - &self.step - } else { - &self.stop - &remainder - }; - - match self.step.sign() { - Sign::Plus => PyRange { - start, - stop: &self.start - 1, - step: -&self.step, - }, - Sign::Minus => PyRange { - start, - stop: &self.start + 1, - step: -&self.step, - }, - Sign::NoSign => unreachable!(), - } - } - - pub fn repr(&self) -> String { - if self.step == BigInt::one() { - format!("range({}, {})", self.start, self.stop) - } else { - format!("range({}, {}, {})", self.start, self.stop, self.step) - } - } } pub fn get_value(obj: &PyObjectRef) -> PyRange { @@ -165,20 +98,20 @@ pub fn init(context: &PyContext) { When step is given, it specifies the increment (or decrement)."; extend_class!(context, range_type, { - "__bool__" => context.new_rustfunc(range_bool), - "__contains__" => context.new_rustfunc(range_contains), + "__bool__" => context.new_rustfunc(PyRangeRef::bool), + "__contains__" => context.new_rustfunc(PyRangeRef::contains), "__doc__" => context.new_str(range_doc.to_string()), "__getitem__" => context.new_rustfunc(PyRangeRef::getitem), - "__iter__" => context.new_rustfunc(range_iter), - "__len__" => context.new_rustfunc(range_len), + "__iter__" => context.new_rustfunc(PyRangeRef::iter), + "__len__" => context.new_rustfunc(PyRangeRef::len), "__new__" => context.new_rustfunc(range_new), - "__repr__" => context.new_rustfunc(range_repr), - "__reversed__" => context.new_rustfunc(range_reversed), - "count" => context.new_rustfunc(range_count), - "index" => context.new_rustfunc(range_index), - "start" => context.new_property(range_start), - "step" => context.new_property(range_step), - "stop" => context.new_property(range_stop) + "__repr__" => context.new_rustfunc(PyRangeRef::repr), + "__reversed__" => context.new_rustfunc(PyRangeRef::reversed), + "count" => context.new_rustfunc(PyRangeRef::count), + "index" => context.new_rustfunc(PyRangeRef::index), + "start" => context.new_property(PyRangeRef::start), + "stop" => context.new_property(PyRangeRef::stop), + "step" => context.new_property(PyRangeRef::step), }); } @@ -187,9 +120,9 @@ type PyRangeRef = PyRef; impl PyRangeRef { fn new(cls: PyClassRef, stop: PyIntRef, vm: &VirtualMachine) -> PyResult { PyRange { - start: Zero::zero(), - stop: stop.value.clone(), - step: One::one(), + start: PyInt::new(BigInt::zero()).into_ref(vm), + stop: stop.clone(), + step: PyInt::new(BigInt::one()).into_ref(vm), } .into_ref_with_type(vm, cls) } @@ -202,29 +135,138 @@ impl PyRangeRef { vm: &VirtualMachine, ) -> PyResult { PyRange { - start: start.value.clone(), - stop: stop.value.clone(), + start: start, + stop: stop, step: step .into_option() - .map(|i| i.value.clone()) - .unwrap_or_else(One::one), + .unwrap_or_else(|| PyInt::new(BigInt::one()).into_ref(vm)), } .into_ref_with_type(vm, cls) } + fn start(self, _vm: &VirtualMachine) -> PyIntRef { + self.start.clone() + } + + fn stop(self, _vm: &VirtualMachine) -> PyIntRef { + self.stop.clone() + } + + fn step(self, _vm: &VirtualMachine) -> PyIntRef { + self.step.clone() + } + + fn iter(self: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { + PyIteratorValue { + position: Cell::new(0), + iterated_obj: self.into_object(), + } + } + + fn reversed(self: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + + // compute the last element that is actually contained within the range + // this is the new start + let remainder = ((stop - start) % step).abs(); + let new_start = if remainder.is_zero() { + stop - step + } else { + stop - &remainder + }; + + let new_stop: BigInt = match step.sign() { + Sign::Plus => start - 1, + Sign::Minus => start + 1, + Sign::NoSign => unreachable!(), + }; + + let reversed = PyRange { + start: PyInt::new(new_start).into_ref(vm), + stop: PyInt::new(new_stop).into_ref(vm), + step: PyInt::new(-step).into_ref(vm), + }; + + PyIteratorValue { + position: Cell::new(0), + iterated_obj: reversed.into_ref(vm).into_object(), + } + } + + fn len(self, _vm: &VirtualMachine) -> PyInt { + let start = self.start.as_bigint(); + let stop = self.stop.as_bigint(); + let step = self.step.as_bigint(); + + match step.sign() { + Sign::Plus if start < stop => PyInt::new((stop - start - 1usize) / step + 1), + Sign::Minus if start > stop => PyInt::new((start - stop - 1usize) / (-step) + 1), + Sign::Plus | Sign::Minus => PyInt::new(0), + Sign::NoSign => unreachable!(), + } + } + + fn repr(self, _vm: &VirtualMachine) -> String { + if self.step.as_bigint().is_one() { + format!("range({}, {})", self.start, self.stop) + } else { + format!("range({}, {}, {})", self.start, self.stop, self.step) + } + } + + fn bool(self, _vm: &VirtualMachine) -> bool { + !self.is_empty() + } + + fn contains(self, needle: PyObjectRef, _vm: &VirtualMachine) -> bool { + if let Ok(int) = needle.downcast::() { + match self.offset(int.as_bigint()) { + Some(ref offset) => offset.is_multiple_of(self.step.as_bigint()), + None => false, + } + } else { + false + } + } + + fn index(self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { + if let Ok(int) = needle.downcast::() { + match self.index_of(int.as_bigint()) { + Some(idx) => Ok(PyInt::new(idx)), + None => Err(vm.new_value_error(format!("{} is not in range", int))), + } + } else { + Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) + } + } + + fn count(self, item: PyObjectRef, _vm: &VirtualMachine) -> PyInt { + if let Ok(int) = item.downcast::() { + if self.index_of(int.as_bigint()).is_some() { + PyInt::new(1) + } else { + PyInt::new(0) + } + } else { + PyInt::new(0) + } + } + fn getitem(self, subscript: Either, vm: &VirtualMachine) -> PyResult { match subscript { Either::A(index) => { - if let Some(value) = self.get(index.value.clone()) { + if let Some(value) = self.get(index.as_bigint()) { Ok(PyInt::new(value).into_ref(vm).into_object()) } else { Err(vm.new_index_error("range object index out of range".to_string())) } } Either::B(slice) => { - let new_start = if let Some(int) = slice.start.clone() { + let new_start = if let Some(int) = slice.start.as_ref() { if let Some(i) = self.get(int) { - i + PyInt::new(i).into_ref(vm) } else { self.start.clone() } @@ -232,9 +274,9 @@ impl PyRangeRef { self.start.clone() }; - let new_end = if let Some(int) = slice.stop.clone() { + let new_end = if let Some(int) = slice.stop.as_ref() { if let Some(i) = self.get(int) { - i + PyInt::new(i).into_ref(vm) } else { self.stop.clone() } @@ -242,8 +284,8 @@ impl PyRangeRef { self.stop.clone() }; - let new_step = if let Some(int) = slice.step.clone() { - int * self.step.clone() + let new_step = if let Some(int) = slice.step.as_ref() { + PyInt::new(int * self.step.as_bigint()).into_ref(vm) } else { self.step.clone() }; @@ -271,115 +313,3 @@ fn range_new(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(range.into_object()) } - -fn range_iter(range: PyRangeRef, _vm: &VirtualMachine) -> PyIteratorValue { - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_object(), - } -} - -fn range_reversed(zelf: PyRangeRef, vm: &VirtualMachine) -> PyIteratorValue { - let range = zelf.reversed(); - - PyIteratorValue { - position: Cell::new(0), - iterated_obj: range.into_ref(vm).into_object(), - } -} - -fn range_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - - if let Some(len) = get_value(zelf).try_len() { - Ok(vm.ctx.new_int(len)) - } else { - Err(vm.new_overflow_error("Python int too large to convert to Rust usize".to_string())) - } -} - -fn range_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - - let repr = get_value(zelf).repr(); - - Ok(vm.ctx.new_str(repr)) -} - -fn range_bool(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - - let len = get_value(zelf).len(); - - Ok(vm.ctx.new_bool(len > 0)) -} - -fn range_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] - ); - - let range = get_value(zelf); - - let result = if objtype::isinstance(needle, &vm.ctx.int_type()) { - range.contains(&objint::get_value(needle)) - } else { - false - }; - - Ok(vm.ctx.new_bool(result)) -} - -fn range_index(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (needle, None)] - ); - - let range = get_value(zelf); - - if objtype::isinstance(needle, &vm.ctx.int_type()) { - let needle = objint::get_value(needle); - - match range.index_of(&needle) { - Some(idx) => Ok(vm.ctx.new_int(idx)), - None => Err(vm.new_value_error(format!("{} is not in range", needle))), - } - } else { - Err(vm.new_value_error("sequence.index(x): x not in sequence".to_string())) - } -} - -fn range_count(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(zelf, Some(vm.ctx.range_type())), (item, None)] - ); - - let range = get_value(zelf); - - if objtype::isinstance(item, &vm.ctx.int_type()) { - Ok(vm.ctx.new_int(range.count(&objint::get_value(item)))) - } else { - Ok(vm.ctx.new_int(0)) - } -} - -fn range_start(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).start)) -} - -fn range_stop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).stop)) -} - -fn range_step(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!(vm, args, required = [(zelf, Some(vm.ctx.range_type()))]); - Ok(vm.ctx.new_int(get_value(zelf).step)) -} diff --git a/vm/src/obj/objsequence.rs b/vm/src/obj/objsequence.rs index 83d0f9f9f..80f4bf40f 100644 --- a/vm/src/obj/objsequence.rs +++ b/vm/src/obj/objsequence.rs @@ -144,7 +144,7 @@ pub fn get_item( subscript: PyObjectRef, ) -> PyResult { if let Some(i) = subscript.payload::() { - return match i.value.to_i32() { + return match i.as_bigint().to_i32() { Some(value) => { if let Some(pos_index) = elements.to_vec().get_pos(value) { let obj = elements[pos_index].clone(); @@ -164,11 +164,13 @@ pub fn get_item( Ok(PyObject::new( PyList::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.type_pyref(), + None, )) } else if sequence.payload::().is_some() { Ok(PyObject::new( PyTuple::from(elements.to_vec().get_slice_items(vm, &subscript)?), sequence.type_pyref(), + None, )) } else { panic!("sequence get_item called for non-sequence") diff --git a/vm/src/obj/objset.rs b/vm/src/obj/objset.rs index 33a445531..7071cc6e4 100644 --- a/vm/src/obj/objset.rs +++ b/vm/src/obj/objset.rs @@ -9,13 +9,14 @@ use std::hash::{Hash, Hasher}; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ - PyContext, PyIteratorValue, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, + PyContext, PyIteratorValue, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TypeProtocol, }; use crate::vm::{ReprGuard, VirtualMachine}; use super::objbool; use super::objint; use super::objiter; +use super::objtype; use super::objtype::PyClassRef; #[derive(Default)] @@ -24,6 +25,12 @@ pub struct PySet { } pub type PySetRef = PyRef; +#[derive(Default)] +pub struct PyFrozenSet { + elements: HashMap, +} +pub type PyFrozenSetRef = PyRef; + impl fmt::Debug for PySet { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // TODO: implement more detailed, non-recursive Debug formatter @@ -31,14 +38,65 @@ impl fmt::Debug for PySet { } } +impl fmt::Debug for PyFrozenSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // TODO: implement more detailed, non-recursive Debug formatter + f.write_str("frozenset") + } +} + impl PyValue for PySet { fn class(vm: &VirtualMachine) -> PyClassRef { vm.ctx.set_type() } } +impl PyValue for PyFrozenSet { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.ctx.frozenset_type() + } +} + pub fn get_elements(obj: &PyObjectRef) -> HashMap { - obj.payload::().unwrap().elements.borrow().clone() + if let Some(set) = obj.payload::() { + return set.elements.borrow().clone(); + } else if let Some(frozenset) = obj.payload::() { + return frozenset.elements.clone(); + } + panic!("Not frozenset or set"); +} + +fn validate_set_or_frozenset(vm: &VirtualMachine, cls: PyClassRef) -> PyResult<()> { + if !(objtype::issubclass(&cls, &vm.ctx.set_type()) + || objtype::issubclass(&cls, &vm.ctx.frozenset_type())) + { + return Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))); + } + Ok(()) +} + +fn create_set( + vm: &VirtualMachine, + elements: HashMap, + cls: PyClassRef, +) -> PyResult { + if objtype::issubclass(&cls, &vm.ctx.set_type()) { + Ok(PyObject::new( + PySet { + elements: RefCell::new(elements), + }, + PySet::class(vm), + None, + )) + } else if objtype::issubclass(&cls, &vm.ctx.frozenset_type()) { + Ok(PyObject::new( + PyFrozenSet { elements: elements }, + PyFrozenSet::class(vm), + None, + )) + } else { + Err(vm.new_type_error(format!("{} is not a subtype of set or frozenset", cls))) + } } fn perform_action_with_hash( @@ -152,11 +210,9 @@ fn set_clear(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } /* Create a new object of sub-type of set */ -fn set_new( - cls: PyClassRef, - iterable: OptionalArg, - vm: &VirtualMachine, -) -> PyResult { +fn set_new(cls: PyClassRef, iterable: OptionalArg, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset(vm, cls.clone())?; + let elements: HashMap = match iterable { OptionalArg::Missing => HashMap::new(), OptionalArg::Present(iterable) => { @@ -169,25 +225,22 @@ fn set_new( } }; - PySet { - elements: RefCell::new(elements), - } - .into_ref_with_type(vm, cls) + create_set(vm, elements, cls.clone()) } fn set_len(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { trace!("set.len called with: {:?}", args); - arg_check!(vm, args, required = [(s, Some(vm.ctx.set_type()))]); + arg_check!(vm, args, required = [(s, None)]); + validate_set_or_frozenset(vm, s.type_pyref())?; let elements = get_elements(s); Ok(vm.context().new_int(elements.len())) } -fn set_copy(obj: PySetRef, _vm: &VirtualMachine) -> PySet { +fn set_copy(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { trace!("set.copy called with: {:?}", obj); - let elements = obj.elements.borrow().clone(); - PySet { - elements: RefCell::new(elements), - } + validate_set_or_frozenset(vm, obj.type_pyref())?; + let elements = get_elements(&obj).clone(); + create_set(vm, elements, obj.type_pyref()) } fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -211,11 +264,8 @@ fn set_repr(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { } pub fn set_contains(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { - arg_check!( - vm, - args, - required = [(set, Some(vm.ctx.set_type())), (needle, None)] - ); + arg_check!(vm, args, required = [(set, None), (needle, None)]); + validate_set_or_frozenset(vm, set.type_pyref())?; for element in get_elements(set).iter() { match vm._eq(needle.clone(), element.1.clone()) { Ok(value) => { @@ -281,14 +331,10 @@ fn set_compare_inner( size_func: &Fn(usize, usize) -> bool, swap: bool, ) -> PyResult { - arg_check!( - vm, - args, - required = [ - (zelf, Some(vm.ctx.set_type())), - (other, Some(vm.ctx.set_type())) - ] - ); + arg_check!(vm, args, required = [(zelf, None), (other, None)]); + + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let get_zelf = |swap: bool| -> &PyObjectRef { if swap { @@ -323,47 +369,48 @@ fn set_compare_inner( Ok(vm.new_bool(true)) } -fn set_union(zelf: PySetRef, other: PySetRef, _vm: &VirtualMachine) -> PySet { - let mut elements = zelf.elements.borrow().clone(); - elements.extend(other.elements.borrow().clone()); +fn set_union(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; - PySet { - elements: RefCell::new(elements), - } + let mut elements = get_elements(&zelf).clone(); + elements.extend(get_elements(&other).clone()); + + create_set(vm, elements, zelf.type_pyref()) } -fn set_intersection(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_intersection(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Intersection) } -fn set_difference(zelf: PySetRef, other: PySetRef, vm: &VirtualMachine) -> PyResult { +fn set_difference(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult { set_combine_inner(zelf, other, vm, SetCombineOperation::Difference) } fn set_symmetric_difference( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - for element in other.elements.borrow().iter() { - let value = vm.call_method(zelf.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&other).iter() { + let value = vm.call_method(&zelf, "__contains__", vec![element.1.clone()])?; if !objbool::get_value(&value) { elements.insert(element.0.clone(), element.1.clone()); } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.type_pyref()) } enum SetCombineOperation { @@ -372,15 +419,17 @@ enum SetCombineOperation { } fn set_combine_inner( - zelf: PySetRef, - other: PySetRef, + zelf: PyObjectRef, + other: PyObjectRef, vm: &VirtualMachine, op: SetCombineOperation, -) -> PyResult { +) -> PyResult { + validate_set_or_frozenset(vm, zelf.type_pyref())?; + validate_set_or_frozenset(vm, other.type_pyref())?; let mut elements = HashMap::new(); - for element in zelf.elements.borrow().iter() { - let value = vm.call_method(other.as_object(), "__contains__", vec![element.1.clone()])?; + for element in get_elements(&zelf).iter() { + let value = vm.call_method(&other, "__contains__", vec![element.1.clone()])?; let should_add = match op { SetCombineOperation::Intersection => objbool::get_value(&value), SetCombineOperation::Difference => !objbool::get_value(&value), @@ -390,9 +439,7 @@ fn set_combine_inner( } } - Ok(PySet { - elements: RefCell::new(elements), - }) + create_set(vm, elements, zelf.type_pyref()) } fn set_pop(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { @@ -548,65 +595,43 @@ pub fn init(context: &PyContext) { set(iterable) -> new set object\n\n\ Build an unordered collection of unique elements."; - context.set_attr(set_type, "__contains__", context.new_rustfunc(set_contains)); - context.set_attr(set_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr(set_type, "__new__", context.new_rustfunc(set_new)); - context.set_attr(set_type, "__repr__", context.new_rustfunc(set_repr)); - context.set_attr(set_type, "__eq__", context.new_rustfunc(set_eq)); - context.set_attr(set_type, "__ge__", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "__gt__", context.new_rustfunc(set_gt)); - context.set_attr(set_type, "__le__", context.new_rustfunc(set_le)); - context.set_attr(set_type, "__lt__", context.new_rustfunc(set_lt)); - context.set_attr(set_type, "issubset", context.new_rustfunc(set_le)); - context.set_attr(set_type, "issuperset", context.new_rustfunc(set_ge)); - context.set_attr(set_type, "union", context.new_rustfunc(set_union)); - context.set_attr(set_type, "__or__", context.new_rustfunc(set_union)); - context.set_attr( - set_type, - "intersection", - context.new_rustfunc(set_intersection), - ); - context.set_attr(set_type, "__and__", context.new_rustfunc(set_intersection)); - context.set_attr(set_type, "difference", context.new_rustfunc(set_difference)); - context.set_attr(set_type, "__sub__", context.new_rustfunc(set_difference)); - context.set_attr( - set_type, - "symmetric_difference", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr( - set_type, - "__xor__", - context.new_rustfunc(set_symmetric_difference), - ); - context.set_attr(set_type, "__doc__", context.new_str(set_doc.to_string())); - context.set_attr(set_type, "add", context.new_rustfunc(set_add)); - context.set_attr(set_type, "remove", context.new_rustfunc(set_remove)); - context.set_attr(set_type, "discard", context.new_rustfunc(set_discard)); - context.set_attr(set_type, "clear", context.new_rustfunc(set_clear)); - context.set_attr(set_type, "copy", context.new_rustfunc(set_copy)); - context.set_attr(set_type, "pop", context.new_rustfunc(set_pop)); - context.set_attr(set_type, "update", context.new_rustfunc(set_update)); - context.set_attr(set_type, "__ior__", context.new_rustfunc(set_ior)); - context.set_attr( - set_type, - "intersection_update", - context.new_rustfunc(set_intersection_update), - ); - context.set_attr(set_type, "__iand__", context.new_rustfunc(set_iand)); - context.set_attr( - set_type, - "difference_update", - context.new_rustfunc(set_difference_update), - ); - context.set_attr(set_type, "__isub__", context.new_rustfunc(set_isub)); - context.set_attr( - set_type, - "symmetric_difference_update", - context.new_rustfunc(set_symmetric_difference_update), - ); - context.set_attr(set_type, "__ixor__", context.new_rustfunc(set_ixor)); - context.set_attr(set_type, "__iter__", context.new_rustfunc(set_iter)); + extend_class!(context, set_type, { + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__new__" => context.new_rustfunc(set_new), + "__repr__" => context.new_rustfunc(set_repr), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__doc__" => context.new_str(set_doc.to_string()), + "add" => context.new_rustfunc(set_add), + "remove" => context.new_rustfunc(set_remove), + "discard" => context.new_rustfunc(set_discard), + "clear" => context.new_rustfunc(set_clear), + "copy" => context.new_rustfunc(set_copy), + "pop" => context.new_rustfunc(set_pop), + "update" => context.new_rustfunc(set_update), + "__ior__" => context.new_rustfunc(set_ior), + "intersection_update" => context.new_rustfunc(set_intersection_update), + "__iand__" => context.new_rustfunc(set_iand), + "difference_update" => context.new_rustfunc(set_difference_update), + "__isub__" => context.new_rustfunc(set_isub), + "symmetric_difference_update" => context.new_rustfunc(set_symmetric_difference_update), + "__ixor__" => context.new_rustfunc(set_ixor), + "__iter__" => context.new_rustfunc(set_iter) + }); let frozenset_type = &context.frozenset_type; @@ -614,20 +639,27 @@ pub fn init(context: &PyContext) { frozenset(iterable) -> frozenset object\n\n\ Build an immutable unordered collection of unique elements."; - context.set_attr( - frozenset_type, - "__contains__", - context.new_rustfunc(set_contains), - ); - context.set_attr(frozenset_type, "__len__", context.new_rustfunc(set_len)); - context.set_attr( - frozenset_type, - "__doc__", - context.new_str(frozenset_doc.to_string()), - ); - context.set_attr( - frozenset_type, - "__repr__", - context.new_rustfunc(frozenset_repr), - ); + extend_class!(context, frozenset_type, { + "__new__" => context.new_rustfunc(set_new), + "__eq__" => context.new_rustfunc(set_eq), + "__ge__" => context.new_rustfunc(set_ge), + "__gt__" => context.new_rustfunc(set_gt), + "__le__" => context.new_rustfunc(set_le), + "__lt__" => context.new_rustfunc(set_lt), + "issubset" => context.new_rustfunc(set_le), + "issuperset" => context.new_rustfunc(set_ge), + "union" => context.new_rustfunc(set_union), + "__or__" => context.new_rustfunc(set_union), + "intersection" => context.new_rustfunc(set_intersection), + "__and__" => context.new_rustfunc(set_intersection), + "difference" => context.new_rustfunc(set_difference), + "__sub__" => context.new_rustfunc(set_difference), + "symmetric_difference" => context.new_rustfunc(set_symmetric_difference), + "__xor__" => context.new_rustfunc(set_symmetric_difference), + "__contains__" => context.new_rustfunc(set_contains), + "__len__" => context.new_rustfunc(set_len), + "__doc__" => context.new_str(frozenset_doc.to_string()), + "__repr__" => context.new_rustfunc(frozenset_repr), + "copy" => context.new_rustfunc(set_copy) + }); } diff --git a/vm/src/obj/objtype.rs b/vm/src/obj/objtype.rs index 89bd8f34a..d87ca3843 100644 --- a/vm/src/obj/objtype.rs +++ b/vm/src/obj/objtype.rs @@ -22,6 +22,7 @@ pub struct PyClass { pub name: String, pub mro: Vec, pub subclasses: RefCell>, + pub attributes: RefCell, } impl fmt::Display for PyClass { @@ -109,8 +110,8 @@ impl PyClassRef { format!("", self.name) } - fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { - vm.new_dict() + fn prepare(_name: PyStringRef, _bases: PyObjectRef, vm: &VirtualMachine) -> PyDictRef { + vm.ctx.new_dict() } fn getattribute(self, name_ref: PyStringRef, vm: &VirtualMachine) -> PyResult { @@ -149,6 +150,25 @@ impl PyClassRef { } } + fn set_attr( + self, + attr_name: PyStringRef, + value: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult<()> { + if let Some(attr) = class_get_attr(&self.type_pyref(), &attr_name.value) { + if let Some(descriptor) = class_get_attr(&attr.type_pyref(), "__set__") { + vm.invoke(descriptor, vec![attr, self.into_object(), value])?; + return Ok(()); + } + } + + self.attributes + .borrow_mut() + .insert(attr_name.to_string(), value); + Ok(()) + } + fn subclasses(self, _vm: &VirtualMachine) -> PyList { let mut subclasses = self.subclasses.borrow_mut(); subclasses.retain(|x| x.upgrade().is_some()); @@ -181,6 +201,7 @@ pub fn init(ctx: &PyContext) { "__repr__" => ctx.new_rustfunc(PyClassRef::repr), "__prepare__" => ctx.new_rustfunc(PyClassRef::prepare), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), + "__setattr__" => ctx.new_rustfunc(PyClassRef::set_attr), "__subclasses__" => ctx.new_rustfunc(PyClassRef::subclasses), "__getattribute__" => ctx.new_rustfunc(PyClassRef::getattribute), "__instancecheck__" => ctx.new_rustfunc(PyClassRef::instance_check), @@ -260,32 +281,13 @@ pub fn type_call(class: PyClassRef, args: Args, kwargs: KwArgs, vm: &VirtualMach Ok(obj) } -// Very private helper function for class_get_attr -fn class_get_attr_in_dict(class: &PyClassRef, attr_name: &str) -> Option { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().get(attr_name).cloned() - } else { - panic!("Only classes should be in MRO!"); - } -} - -// Very private helper function for class_has_attr -fn class_has_attr_in_dict(class: &PyClassRef, attr_name: &str) -> bool { - if let Some(ref dict) = class.as_object().dict { - dict.borrow().contains_key(attr_name) - } else { - panic!("All classes are expected to have dicts!"); - } -} - // This is the internal get_attr implementation for fast lookup on a class. -pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option { - let mro = &zelf.mro; - if let Some(item) = class_get_attr_in_dict(zelf, attr_name) { +pub fn class_get_attr(class: &PyClassRef, attr_name: &str) -> Option { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } - for class in mro { - if let Some(item) = class_get_attr_in_dict(class, attr_name) { + for class in &class.mro { + if let Some(item) = class.attributes.borrow().get(attr_name).cloned() { return Some(item); } } @@ -293,12 +295,12 @@ pub fn class_get_attr(zelf: &PyClassRef, attr_name: &str) -> Option } // This is the internal has_attr implementation for fast lookup on a class. -pub fn class_has_attr(zelf: &PyClassRef, attr_name: &str) -> bool { - class_has_attr_in_dict(zelf, attr_name) - || zelf +pub fn class_has_attr(class: &PyClassRef, attr_name: &str) -> bool { + class.attributes.borrow().contains_key(attr_name) + || class .mro .iter() - .any(|d| class_has_attr_in_dict(d, attr_name)) + .any(|c| c.attributes.borrow().contains_key(attr_name)) } pub fn get_attributes(cls: PyClassRef) -> PyAttributes { @@ -309,10 +311,8 @@ pub fn get_attributes(cls: PyClassRef) -> PyAttributes { base_classes.reverse(); for bc in base_classes { - if let Some(ref dict) = &bc.as_object().dict { - for (name, value) in dict.borrow().iter() { - attributes.insert(name.to_string(), value.clone()); - } + for (name, value) in bc.attributes.borrow().iter() { + attributes.insert(name.to_string(), value.clone()); } } @@ -374,8 +374,9 @@ pub fn new( name: String::from(name), mro, subclasses: RefCell::new(vec![]), + attributes: RefCell::new(dict), }, - dict: Some(RefCell::new(dict)), + dict: None, typ, } .into_ref(); diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e4488bdb4..78e82c51b 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -24,7 +24,7 @@ use crate::obj::objclassmethod; use crate::obj::objcode; use crate::obj::objcode::PyCodeRef; use crate::obj::objcomplex::{self, PyComplex}; -use crate::obj::objdict::{self, PyDict}; +use crate::obj::objdict::{self, PyDict, PyDictRef}; use crate::obj::objellipsis; use crate::obj::objenumerate; use crate::obj::objfilter; @@ -186,22 +186,24 @@ fn init_type_hierarchy() -> (PyClassRef, PyClassRef) { let (type_type, object_type) = unsafe { let object_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("object"), mro: vec![], subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); let type_type = PyObject { typ: mem::uninitialized(), // ! - dict: Some(RefCell::new(PyAttributes::new())), + dict: None, payload: PyClass { name: String::from("type"), mro: vec![object_type.clone().downcast().unwrap()], subclasses: RefCell::new(vec![]), + attributes: RefCell::new(PyAttributes::new()), }, } .into_ref(); @@ -269,7 +271,7 @@ impl PyContext { fn create_object(payload: T, cls: &PyClassRef) -> PyRef { PyRef { - obj: PyObject::new(payload, cls.clone()), + obj: PyObject::new(payload, cls.clone(), None), _payload: PhantomData, } } @@ -519,32 +521,32 @@ impl PyContext { self.object.clone() } - pub fn new_object(&self) -> PyObjectRef { - self.new_instance(self.object.clone(), None) - } - pub fn new_int>(&self, i: T) -> PyObjectRef { - PyObject::new(PyInt::new(i), self.int_type()) + PyObject::new(PyInt::new(i), self.int_type(), None) } pub fn new_float(&self, value: f64) -> PyObjectRef { - PyObject::new(PyFloat::from(value), self.float_type()) + PyObject::new(PyFloat::from(value), self.float_type(), None) } pub fn new_complex(&self, value: Complex64) -> PyObjectRef { - PyObject::new(PyComplex::from(value), self.complex_type()) + PyObject::new(PyComplex::from(value), self.complex_type(), None) } pub fn new_str(&self, s: String) -> PyObjectRef { - PyObject::new(objstr::PyString { value: s }, self.str_type()) + PyObject::new(objstr::PyString { value: s }, self.str_type(), None) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { - PyObject::new(objbytes::PyBytes::new(data), self.bytes_type()) + PyObject::new(objbytes::PyBytes::new(data), self.bytes_type(), None) } pub fn new_bytearray(&self, data: Vec) -> PyObjectRef { - PyObject::new(objbytearray::PyByteArray::new(data), self.bytearray_type()) + PyObject::new( + objbytearray::PyByteArray::new(data), + self.bytearray_type(), + None, + ) } pub fn new_bool(&self, b: bool) -> PyObjectRef { @@ -556,21 +558,23 @@ impl PyContext { } pub fn new_tuple(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyTuple::from(elements), self.tuple_type()) + PyObject::new(PyTuple::from(elements), self.tuple_type(), None) } pub fn new_list(&self, elements: Vec) -> PyObjectRef { - PyObject::new(PyList::from(elements), self.list_type()) + PyObject::new(PyList::from(elements), self.list_type(), None) } pub fn new_set(&self) -> PyObjectRef { // Initialized empty, as calling __hash__ is required for adding each object to the set // which requires a VM context - this is done in the objset code itself. - PyObject::new(PySet::default(), self.set_type()) + PyObject::new(PySet::default(), self.set_type(), None) } - pub fn new_dict(&self) -> PyObjectRef { - PyObject::new(PyDict::default(), self.dict_type()) + pub fn new_dict(&self) -> PyDictRef { + PyObject::new(PyDict::default(), self.dict_type(), None) + .downcast() + .unwrap() } pub fn new_class(&self, name: &str, base: PyClassRef) -> PyClassRef { @@ -578,16 +582,16 @@ impl PyContext { } pub fn new_scope(&self) -> Scope { - Scope::new(None, self.new_dict()) + Scope::new(None, self.new_dict().into_object()) } - pub fn new_module(&self, name: &str, dict: PyObjectRef) -> PyObjectRef { + pub fn new_module(&self, name: &str, dict: PyDictRef) -> PyObjectRef { PyObject::new( PyModule { name: name.to_string(), - dict, }, self.module_type.clone(), + Some(dict), ) } @@ -598,6 +602,7 @@ impl PyContext { PyObject::new( PyBuiltinFunction::new(f.into_func()), self.builtin_function_or_method_type(), + None, ) } @@ -609,7 +614,7 @@ impl PyContext { } pub fn new_code_object(&self, code: bytecode::CodeObject) -> PyObjectRef { - PyObject::new(objcode::PyCode::new(code), self.code_type()) + PyObject::new(objcode::PyCode::new(code), self.code_type(), None) } pub fn new_function( @@ -621,18 +626,22 @@ impl PyContext { PyObject::new( PyFunction::new(code_obj, scope, defaults), self.function_type(), + Some(self.new_dict()), ) } pub fn new_bound_method(&self, function: PyObjectRef, object: PyObjectRef) -> PyObjectRef { - PyObject::new(PyMethod::new(object, function), self.bound_method_type()) + PyObject::new( + PyMethod::new(object, function), + self.bound_method_type(), + None, + ) } - pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { - let dict = dict.unwrap_or_default(); + pub fn new_instance(&self, class: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ: class, - dict: Some(RefCell::new(dict)), + dict: dict, payload: objobject::PyInstance, } .into_ref() @@ -655,11 +664,12 @@ impl PyContext { value: V, ) { let obj = obj.into(); - if let Some(PyModule { ref dict, .. }) = obj.payload::() { - dict.set_item(self, attr_name, value.into()) - } else if let Some(ref dict) = obj.dict { - dict.borrow_mut() + if let Some(PyClass { ref attributes, .. }) = obj.payload::() { + attributes + .borrow_mut() .insert(attr_name.to_string(), value.into()); + } else if let Some(ref dict) = obj.dict { + dict.set_item(self, attr_name, value.into()); } else { unimplemented!("set_attr unimplemented for: {:?}", obj); }; @@ -701,7 +711,7 @@ where T: ?Sized + PyObjectPayload, { pub typ: PyClassRef, - pub dict: Option>, // __dict__ member + pub dict: Option, // __dict__ member pub payload: T, } @@ -911,8 +921,6 @@ impl DictProtocol for PyObjectRef { fn get_item(&self, k: &str) -> Option { if let Some(dict) = self.payload::() { objdict::content_get_key_str(&dict.entries.borrow(), k) - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.get_item(k) } else { panic!("TODO {:?}", k) } @@ -921,8 +929,6 @@ impl DictProtocol for PyObjectRef { fn get_key_value_pairs(&self) -> Vec<(PyObjectRef, PyObjectRef)> { if self.payload_is::() { objdict::get_key_value_pairs(self) - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.get_key_value_pairs() } else { panic!("TODO") } @@ -933,8 +939,6 @@ impl DictProtocol for PyObjectRef { if let Some(dict) = self.payload::() { let key = ctx.new_str(key.to_string()); objdict::set_item_in_content(&mut dict.entries.borrow_mut(), &key, &v); - } else if let Some(PyModule { ref dict, .. }) = self.payload::() { - dict.set_item(ctx, key, v); } else { panic!("TODO {:?}", self); } @@ -1115,7 +1119,7 @@ where T: PyValue + Sized, { fn into_pyobject(self, vm: &VirtualMachine) -> PyResult { - Ok(PyObject::new(self, T::class(vm))) + Ok(PyObject::new(self, T::class(vm), None)) } } @@ -1137,19 +1141,10 @@ impl PyObject where T: Sized + PyObjectPayload, { - pub fn new(payload: T, typ: PyClassRef) -> PyObjectRef { + pub fn new(payload: T, typ: PyClassRef, dict: Option) -> PyObjectRef { PyObject { typ, - dict: Some(RefCell::new(PyAttributes::new())), - payload, - } - .into_ref() - } - - pub fn new_without_dict(payload: T, typ: PyClassRef) -> PyObjectRef { - PyObject { - typ, - dict: None, + dict: dict, payload, } .into_ref() @@ -1178,7 +1173,7 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref(self, vm: &VirtualMachine) -> PyRef { PyRef { - obj: PyObject::new(self, Self::class(vm)), + obj: PyObject::new(self, Self::class(vm), None), _payload: PhantomData, } } @@ -1186,8 +1181,13 @@ pub trait PyValue: fmt::Debug + Sized + 'static { fn into_ref_with_type(self, vm: &VirtualMachine, cls: PyClassRef) -> PyResult> { let class = Self::class(vm); if objtype::issubclass(&cls, &class) { + let dict = if cls.is(&class) { + None + } else { + Some(vm.ctx.new_dict()) + }; Ok(PyRef { - obj: PyObject::new(self, cls), + obj: PyObject::new(self, cls, dict), _payload: PhantomData, }) } else { diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index a3010a519..0011407ab 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -11,9 +11,20 @@ use rustpython_parser::{ast, parser}; use crate::function::PyFuncArgs; use crate::obj::objstr; -use crate::pyobject::{PyContext, PyObjectRef, PyResult, TypeProtocol}; +use crate::obj::objtype::PyClassRef; +use crate::pyobject::{PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol}; use crate::vm::VirtualMachine; +#[derive(Debug)] +struct AstNode; +// type AstNodeRef = PyRef; + +impl PyValue for AstNode { + fn class(vm: &VirtualMachine) -> PyClassRef { + vm.class("ast", "AST") + } +} + /* * Idea: maybe we can create a sort of struct with some helper functions? struct AstToPyAst { @@ -30,35 +41,35 @@ impl AstToPyAst { } */ -fn program_to_ast(ctx: &PyContext, program: &ast::Program) -> PyObjectRef { +fn program_to_ast(vm: &VirtualMachine, program: &ast::Program) -> PyObjectRef { let mut body = vec![]; for statement in &program.statements { - body.push(statement_to_ast(ctx, statement)); + body.push(statement_to_ast(&vm, statement)); } // TODO: create Module node: // let ast_node = ctx.new_instance(this.Module); - let ast_node = ctx.new_object(); - let py_body = ctx.new_list(body); - ctx.set_attr(&ast_node, "body", py_body); + let ast_node = create_node(vm, "program"); + let py_body = vm.ctx.new_list(body); + vm.ctx.set_attr(&ast_node, "body", py_body); ast_node } // Create a node class instance -fn create_node(ctx: &PyContext, _name: &str) -> PyObjectRef { +fn create_node(vm: &VirtualMachine, _name: &str) -> PyObjectRef { // TODO: instantiate a class of type given by name // TODO: lookup in the current module? - ctx.new_object() + PyObject::new(AstNode, AstNode::class(vm), Some(vm.ctx.new_dict())) } -fn statements_to_ast(ctx: &PyContext, statements: &[ast::LocatedStatement]) -> PyObjectRef { +fn statements_to_ast(vm: &VirtualMachine, statements: &[ast::LocatedStatement]) -> PyObjectRef { let mut py_statements = vec![]; for statement in statements { - py_statements.push(statement_to_ast(ctx, statement)); + py_statements.push(statement_to_ast(vm, statement)); } - ctx.new_list(py_statements) + vm.ctx.new_list(py_statements) } -fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObjectRef { +fn statement_to_ast(vm: &VirtualMachine, statement: &ast::LocatedStatement) -> PyObjectRef { let node = match &statement.node { ast::Statement::ClassDef { name, @@ -66,17 +77,18 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, .. } => { - let node = create_node(ctx, "ClassDef"); + let node = create_node(vm, "ClassDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); node } ast::Statement::FunctionDef { @@ -86,80 +98,83 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj decorator_list, returns, } => { - let node = create_node(ctx, "FunctionDef"); + let node = create_node(vm, "FunctionDef"); // Set name: - ctx.set_attr(&node, "name", ctx.new_str(name.to_string())); + vm.ctx + .set_attr(&node, "name", vm.ctx.new_str(name.to_string())); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); // Set body: - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_decorator_list = expressions_to_ast(ctx, decorator_list); - ctx.set_attr(&node, "decorator_list", py_decorator_list); + let py_decorator_list = expressions_to_ast(vm, decorator_list); + vm.ctx.set_attr(&node, "decorator_list", py_decorator_list); let py_returns = if let Some(hint) = returns { - expression_to_ast(ctx, hint) + expression_to_ast(vm, hint) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "returns", py_returns); + vm.ctx.set_attr(&node, "returns", py_returns); node } - ast::Statement::Continue => create_node(ctx, "Continue"), - ast::Statement::Break => create_node(ctx, "Break"), - ast::Statement::Pass => create_node(ctx, "Pass"), + ast::Statement::Continue => create_node(vm, "Continue"), + ast::Statement::Break => create_node(vm, "Break"), + ast::Statement::Pass => create_node(vm, "Pass"), ast::Statement::Assert { test, msg } => { - let node = create_node(ctx, "Pass"); + let node = create_node(vm, "Pass"); - ctx.set_attr(&node, "test", expression_to_ast(ctx, test)); + vm.ctx.set_attr(&node, "test", expression_to_ast(vm, test)); let py_msg = match msg { - Some(msg) => expression_to_ast(ctx, msg), - None => ctx.none(), + Some(msg) => expression_to_ast(vm, msg), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "msg", py_msg); + vm.ctx.set_attr(&node, "msg", py_msg); node } ast::Statement::Delete { targets } => { - let node = create_node(ctx, "Delete"); + let node = create_node(vm, "Delete"); - let py_targets = - ctx.new_tuple(targets.iter().map(|v| expression_to_ast(ctx, v)).collect()); - ctx.set_attr(&node, "targets", py_targets); + let py_targets = vm + .ctx + .new_tuple(targets.iter().map(|v| expression_to_ast(vm, v)).collect()); + vm.ctx.set_attr(&node, "targets", py_targets); node } ast::Statement::Return { value } => { - let node = create_node(ctx, "Return"); + let node = create_node(vm, "Return"); let py_value = if let Some(value) = value { - ctx.new_tuple(value.iter().map(|v| expression_to_ast(ctx, v)).collect()) + vm.ctx + .new_tuple(value.iter().map(|v| expression_to_ast(vm, v)).collect()) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Statement::If { test, body, orelse } => { - let node = create_node(ctx, "If"); + let node = create_node(vm, "If"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } @@ -169,49 +184,49 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj body, orelse, } => { - let node = create_node(ctx, "For"); + let node = create_node(vm, "For"); - let py_target = expression_to_ast(ctx, target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expressions_to_ast(ctx, iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expressions_to_ast(vm, iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::While { test, body, orelse } => { - let node = create_node(ctx, "While"); + let node = create_node(vm, "While"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = statements_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = statements_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); let py_orelse = if let Some(orelse) = orelse { - statements_to_ast(ctx, orelse) + statements_to_ast(vm, orelse) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "orelse", py_orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Statement::Expression { expression } => { - let node = create_node(ctx, "Expr"); + let node = create_node(vm, "Expr"); - let value = expression_to_ast(ctx, expression); - ctx.set_attr(&node, "value", value); + let value = expression_to_ast(vm, expression); + vm.ctx.set_attr(&node, "value", value); node } @@ -221,38 +236,38 @@ fn statement_to_ast(ctx: &PyContext, statement: &ast::LocatedStatement) -> PyObj }; // set lineno on node: - let lineno = ctx.new_int(statement.location.get_row()); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(statement.location.get_row()); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn expressions_to_ast(ctx: &PyContext, expressions: &[ast::Expression]) -> PyObjectRef { +fn expressions_to_ast(vm: &VirtualMachine, expressions: &[ast::Expression]) -> PyObjectRef { let mut py_expression_nodes = vec![]; for expression in expressions { - py_expression_nodes.push(expression_to_ast(ctx, expression)); + py_expression_nodes.push(expression_to_ast(vm, expression)); } - ctx.new_list(py_expression_nodes) + vm.ctx.new_list(py_expression_nodes) } -fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectRef { +fn expression_to_ast(vm: &VirtualMachine, expression: &ast::Expression) -> PyObjectRef { let node = match &expression { ast::Expression::Call { function, args, .. } => { - let node = create_node(ctx, "Call"); + let node = create_node(vm, "Call"); - let py_func_ast = expression_to_ast(ctx, function); - ctx.set_attr(&node, "func", py_func_ast); + let py_func_ast = expression_to_ast(vm, function); + vm.ctx.set_attr(&node, "func", py_func_ast); - let py_args = expressions_to_ast(ctx, args); - ctx.set_attr(&node, "args", py_args); + let py_args = expressions_to_ast(vm, args); + vm.ctx.set_attr(&node, "args", py_args); node } ast::Expression::Binop { a, op, b } => { - let node = create_node(ctx, "BinOp"); + let node = create_node(vm, "BinOp"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -270,15 +285,15 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Operator::BitAnd => "BitAnd", ast::Operator::FloorDiv => "FloorDiv", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_b = expression_to_ast(ctx, b); - ctx.set_attr(&node, "right", py_b); + let py_b = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "right", py_b); node } ast::Expression::Unop { op, a } => { - let node = create_node(ctx, "UnaryOp"); + let node = create_node(vm, "UnaryOp"); let str_op = match op { ast::UnaryOperator::Not => "Not", @@ -286,37 +301,37 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::UnaryOperator::Neg => "USub", ast::UnaryOperator::Pos => "UAdd", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "operand", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "operand", py_a); node } ast::Expression::BoolOp { a, op, b } => { - let node = create_node(ctx, "BoolOp"); + let node = create_node(vm, "BoolOp"); // Attach values: - let py_a = expression_to_ast(ctx, a); - let py_b = expression_to_ast(ctx, b); - let py_values = ctx.new_tuple(vec![py_a, py_b]); - ctx.set_attr(&node, "values", py_values); + let py_a = expression_to_ast(vm, a); + let py_b = expression_to_ast(vm, b); + let py_values = vm.ctx.new_tuple(vec![py_a, py_b]); + vm.ctx.set_attr(&node, "values", py_values); let str_op = match op { ast::BooleanOperator::And => "And", ast::BooleanOperator::Or => "Or", }; - let py_op = ctx.new_str(str_op.to_string()); - ctx.set_attr(&node, "op", py_op); + let py_op = vm.ctx.new_str(str_op.to_string()); + vm.ctx.set_attr(&node, "op", py_op); node } ast::Expression::Compare { a, op, b } => { - let node = create_node(ctx, "Compare"); + let node = create_node(vm, "Compare"); - let py_a = expression_to_ast(ctx, a); - ctx.set_attr(&node, "left", py_a); + let py_a = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "left", py_a); // Operator: let str_op = match op { @@ -331,282 +346,283 @@ fn expression_to_ast(ctx: &PyContext, expression: &ast::Expression) -> PyObjectR ast::Comparison::Is => "Is", ast::Comparison::IsNot => "IsNot", }; - let py_ops = ctx.new_list(vec![ctx.new_str(str_op.to_string())]); - ctx.set_attr(&node, "ops", py_ops); + let py_ops = vm.ctx.new_list(vec![vm.ctx.new_str(str_op.to_string())]); + vm.ctx.set_attr(&node, "ops", py_ops); - let py_b = ctx.new_list(vec![expression_to_ast(ctx, b)]); - ctx.set_attr(&node, "comparators", py_b); + let py_b = vm.ctx.new_list(vec![expression_to_ast(vm, b)]); + vm.ctx.set_attr(&node, "comparators", py_b); node } ast::Expression::Identifier { name } => { - let node = create_node(ctx, "Identifier"); + let node = create_node(vm, "Identifier"); // Id: - let py_name = ctx.new_str(name.clone()); - ctx.set_attr(&node, "id", py_name); + let py_name = vm.ctx.new_str(name.clone()); + vm.ctx.set_attr(&node, "id", py_name); node } ast::Expression::Lambda { args, body } => { - let node = create_node(ctx, "Lambda"); + let node = create_node(vm, "Lambda"); - ctx.set_attr(&node, "args", parameters_to_ast(ctx, args)); + vm.ctx.set_attr(&node, "args", parameters_to_ast(vm, args)); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); node } ast::Expression::IfExpression { test, body, orelse } => { - let node = create_node(ctx, "IfExp"); + let node = create_node(vm, "IfExp"); - let py_test = expression_to_ast(ctx, test); - ctx.set_attr(&node, "test", py_test); + let py_test = expression_to_ast(vm, test); + vm.ctx.set_attr(&node, "test", py_test); - let py_body = expression_to_ast(ctx, body); - ctx.set_attr(&node, "body", py_body); + let py_body = expression_to_ast(vm, body); + vm.ctx.set_attr(&node, "body", py_body); - let py_orelse = expression_to_ast(ctx, orelse); - ctx.set_attr(&node, "orelse", py_orelse); + let py_orelse = expression_to_ast(vm, orelse); + vm.ctx.set_attr(&node, "orelse", py_orelse); node } ast::Expression::Number { value } => { - let node = create_node(ctx, "Num"); + let node = create_node(vm, "Num"); let py_n = match value { - ast::Number::Integer { value } => ctx.new_int(value.clone()), - ast::Number::Float { value } => ctx.new_float(*value), + ast::Number::Integer { value } => vm.ctx.new_int(value.clone()), + ast::Number::Float { value } => vm.ctx.new_float(*value), ast::Number::Complex { real, imag } => { - ctx.new_complex(Complex64::new(*real, *imag)) + vm.ctx.new_complex(Complex64::new(*real, *imag)) } }; - ctx.set_attr(&node, "n", py_n); + vm.ctx.set_attr(&node, "n", py_n); node } ast::Expression::True => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(true)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(true)); node } ast::Expression::False => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.new_bool(false)); + vm.ctx.set_attr(&node, "value", vm.ctx.new_bool(false)); node } ast::Expression::None => { - let node = create_node(ctx, "NameConstant"); + let node = create_node(vm, "NameConstant"); - ctx.set_attr(&node, "value", ctx.none()); + vm.ctx.set_attr(&node, "value", vm.ctx.none()); node } - ast::Expression::Ellipsis => create_node(ctx, "Ellipsis"), + ast::Expression::Ellipsis => create_node(vm, "Ellipsis"), ast::Expression::List { elements } => { - let node = create_node(ctx, "List"); + let node = create_node(vm, "List"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Tuple { elements } => { - let node = create_node(ctx, "Tuple"); + let node = create_node(vm, "Tuple"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Set { elements } => { - let node = create_node(ctx, "Set"); + let node = create_node(vm, "Set"); - let elts = elements.iter().map(|e| expression_to_ast(ctx, e)).collect(); - let py_elts = ctx.new_list(elts); - ctx.set_attr(&node, "elts", py_elts); + let elts = elements.iter().map(|e| expression_to_ast(vm, e)).collect(); + let py_elts = vm.ctx.new_list(elts); + vm.ctx.set_attr(&node, "elts", py_elts); node } ast::Expression::Dict { elements } => { - let node = create_node(ctx, "Dict"); + let node = create_node(vm, "Dict"); let mut keys = Vec::new(); let mut values = Vec::new(); for (k, v) in elements { - keys.push(expression_to_ast(ctx, k)); - values.push(expression_to_ast(ctx, v)); + keys.push(expression_to_ast(vm, k)); + values.push(expression_to_ast(vm, v)); } - let py_keys = ctx.new_list(keys); - ctx.set_attr(&node, "keys", py_keys); + let py_keys = vm.ctx.new_list(keys); + vm.ctx.set_attr(&node, "keys", py_keys); - let py_values = ctx.new_list(values); - ctx.set_attr(&node, "values", py_values); + let py_values = vm.ctx.new_list(values); + vm.ctx.set_attr(&node, "values", py_values); node } ast::Expression::Comprehension { kind, generators } => { let node = match kind.deref() { ast::ComprehensionKind::GeneratorExpression { .. } => { - create_node(ctx, "GeneratorExp") + create_node(vm, "GeneratorExp") } - ast::ComprehensionKind::List { .. } => create_node(ctx, "ListComp"), - ast::ComprehensionKind::Set { .. } => create_node(ctx, "SetComp"), - ast::ComprehensionKind::Dict { .. } => create_node(ctx, "DictComp"), + ast::ComprehensionKind::List { .. } => create_node(vm, "ListComp"), + ast::ComprehensionKind::Set { .. } => create_node(vm, "SetComp"), + ast::ComprehensionKind::Dict { .. } => create_node(vm, "DictComp"), }; let g = generators .iter() - .map(|g| comprehension_to_ast(ctx, g)) + .map(|g| comprehension_to_ast(vm, g)) .collect(); - let py_generators = ctx.new_list(g); - ctx.set_attr(&node, "generators", py_generators); + let py_generators = vm.ctx.new_list(g); + vm.ctx.set_attr(&node, "generators", py_generators); node } ast::Expression::Yield { value } => { - let node = create_node(ctx, "Yield"); + let node = create_node(vm, "Yield"); let py_value = match value { - Some(value) => expression_to_ast(ctx, value), - None => ctx.none(), + Some(value) => expression_to_ast(vm, value), + None => vm.ctx.none(), }; - ctx.set_attr(&node, "value", py_value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::YieldFrom { value } => { - let node = create_node(ctx, "YieldFrom"); + let node = create_node(vm, "YieldFrom"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Subscript { a, b } => { - let node = create_node(ctx, "Subscript"); + let node = create_node(vm, "Subscript"); - let py_value = expression_to_ast(ctx, a); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, a); + vm.ctx.set_attr(&node, "value", py_value); - let py_slice = expression_to_ast(ctx, b); - ctx.set_attr(&node, "slice", py_slice); + let py_slice = expression_to_ast(vm, b); + vm.ctx.set_attr(&node, "slice", py_slice); node } ast::Expression::Attribute { value, name } => { - let node = create_node(ctx, "Attribute"); + let node = create_node(vm, "Attribute"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); - let py_attr = ctx.new_str(name.to_string()); - ctx.set_attr(&node, "attr", py_attr); + let py_attr = vm.ctx.new_str(name.to_string()); + vm.ctx.set_attr(&node, "attr", py_attr); node } ast::Expression::Starred { value } => { - let node = create_node(ctx, "Starred"); + let node = create_node(vm, "Starred"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::Expression::Slice { elements } => { - let node = create_node(ctx, "Slice"); + let node = create_node(vm, "Slice"); - let py_value = expressions_to_ast(ctx, elements); - ctx.set_attr(&node, "bounds", py_value); + let py_value = expressions_to_ast(vm, elements); + vm.ctx.set_attr(&node, "bounds", py_value); node } - ast::Expression::String { value } => string_to_ast(ctx, value), + ast::Expression::String { value } => string_to_ast(vm, value), ast::Expression::Bytes { value } => { - let node = create_node(ctx, "Bytes"); - ctx.set_attr(&node, "s", ctx.new_bytes(value.clone())); + let node = create_node(vm, "Bytes"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_bytes(value.clone())); node } }; // TODO: retrieve correct lineno: - let lineno = ctx.new_int(1); - ctx.set_attr(&node, "lineno", lineno); + let lineno = vm.ctx.new_int(1); + vm.ctx.set_attr(&node, "lineno", lineno); node } -fn parameters_to_ast(ctx: &PyContext, args: &ast::Parameters) -> PyObjectRef { - let node = create_node(ctx, "arguments"); +fn parameters_to_ast(vm: &VirtualMachine, args: &ast::Parameters) -> PyObjectRef { + let node = create_node(vm, "arguments"); - ctx.set_attr( + vm.ctx.set_attr( &node, "args", - ctx.new_list(args.args.iter().map(|a| parameter_to_ast(ctx, a)).collect()), + vm.ctx + .new_list(args.args.iter().map(|a| parameter_to_ast(vm, a)).collect()), ); node } -fn parameter_to_ast(ctx: &PyContext, parameter: &ast::Parameter) -> PyObjectRef { - let node = create_node(ctx, "arg"); +fn parameter_to_ast(vm: &VirtualMachine, parameter: &ast::Parameter) -> PyObjectRef { + let node = create_node(vm, "arg"); - let py_arg = ctx.new_str(parameter.arg.to_string()); - ctx.set_attr(&node, "arg", py_arg); + let py_arg = vm.ctx.new_str(parameter.arg.to_string()); + vm.ctx.set_attr(&node, "arg", py_arg); let py_annotation = if let Some(annotation) = ¶meter.annotation { - expression_to_ast(ctx, annotation) + expression_to_ast(vm, annotation) } else { - ctx.none() + vm.ctx.none() }; - ctx.set_attr(&node, "annotation", py_annotation); + vm.ctx.set_attr(&node, "annotation", py_annotation); node } -fn comprehension_to_ast(ctx: &PyContext, comprehension: &ast::Comprehension) -> PyObjectRef { - let node = create_node(ctx, "comprehension"); +fn comprehension_to_ast(vm: &VirtualMachine, comprehension: &ast::Comprehension) -> PyObjectRef { + let node = create_node(vm, "comprehension"); - let py_target = expression_to_ast(ctx, &comprehension.target); - ctx.set_attr(&node, "target", py_target); + let py_target = expression_to_ast(vm, &comprehension.target); + vm.ctx.set_attr(&node, "target", py_target); - let py_iter = expression_to_ast(ctx, &comprehension.iter); - ctx.set_attr(&node, "iter", py_iter); + let py_iter = expression_to_ast(vm, &comprehension.iter); + vm.ctx.set_attr(&node, "iter", py_iter); - let py_ifs = expressions_to_ast(ctx, &comprehension.ifs); - ctx.set_attr(&node, "ifs", py_ifs); + let py_ifs = expressions_to_ast(vm, &comprehension.ifs); + vm.ctx.set_attr(&node, "ifs", py_ifs); node } -fn string_to_ast(ctx: &PyContext, string: &ast::StringGroup) -> PyObjectRef { +fn string_to_ast(vm: &VirtualMachine, string: &ast::StringGroup) -> PyObjectRef { match string { ast::StringGroup::Constant { value } => { - let node = create_node(ctx, "Str"); - ctx.set_attr(&node, "s", ctx.new_str(value.clone())); + let node = create_node(vm, "Str"); + vm.ctx.set_attr(&node, "s", vm.ctx.new_str(value.clone())); node } ast::StringGroup::FormattedValue { value, .. } => { - let node = create_node(ctx, "FormattedValue"); - let py_value = expression_to_ast(ctx, value); - ctx.set_attr(&node, "value", py_value); + let node = create_node(vm, "FormattedValue"); + let py_value = expression_to_ast(vm, value); + vm.ctx.set_attr(&node, "value", py_value); node } ast::StringGroup::Joined { values } => { - let node = create_node(ctx, "JoinedStr"); - let py_values = ctx.new_list( + let node = create_node(vm, "JoinedStr"); + let py_values = vm.ctx.new_list( values .iter() - .map(|value| string_to_ast(ctx, value)) + .map(|value| string_to_ast(vm, value)) .collect(), ); - ctx.set_attr(&node, "values", py_values); + vm.ctx.set_attr(&node, "values", py_values); node } } @@ -619,7 +635,7 @@ fn ast_parse(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { let internal_ast = parser::parse_program(&source_string) .map_err(|err| vm.new_value_error(format!("{}", err)))?; // source.clone(); - let ast_node = program_to_ast(&vm.ctx, &internal_ast); + let ast_node = program_to_ast(&vm, &internal_ast); Ok(ast_node) } @@ -628,6 +644,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { "parse" => ctx.new_rustfunc(ast_parse), "Module" => py_class!(ctx, "_ast.Module", ctx.object(), {}), "FunctionDef" => py_class!(ctx, "_ast.FunctionDef", ctx.object(), {}), - "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}) + "Call" => py_class!(ctx, "_ast.Call", ctx.object(), {}), + "AST" => py_class!(ctx, "_ast.AST", ctx.object(), {}), }) } diff --git a/vm/src/stdlib/json.rs b/vm/src/stdlib/json.rs index 2c5c95d34..eac3619c2 100644 --- a/vm/src/stdlib/json.rs +++ b/vm/src/stdlib/json.rs @@ -177,7 +177,7 @@ impl<'de> Visitor<'de> for PyObjectDeserializer<'de> { }; dict.set_item(&self.vm.ctx, &key, value); } - Ok(dict) + Ok(dict.into_object()) } fn visit_unit(self) -> Result diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 7713c561d..0ee9798ab 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -141,10 +141,6 @@ impl VirtualMachine { self.ctx.new_bool(b) } - pub fn new_dict(&self) -> PyObjectRef { - self.ctx.new_dict() - } - pub fn new_empty_exception(&self, exc_type: PyClassRef) -> PyResult { info!("New exception created: no msg"); let args = PyFuncArgs::default(); @@ -454,11 +450,11 @@ impl VirtualMachine { // Do we support `**kwargs` ? let kwargs = match code_object.varkeywords { bytecode::Varargs::Named(ref kwargs_name) => { - let d = self.new_dict(); + let d = self.ctx.new_dict().into_object(); locals.set_item(&self.ctx, kwargs_name, d.clone()); Some(d) } - bytecode::Varargs::Unnamed => Some(self.new_dict()), + bytecode::Varargs::Unnamed => Some(self.ctx.new_dict().into_object()), bytecode::Varargs::None => None, }; diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index a309284cf..0e13aeb3a 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -363,6 +363,7 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { doc: window().document().expect("Document missing from window"), }, document_class.clone(), + None, ); let element = py_class!(ctx, "Element", ctx.object(), { diff --git a/wasm/lib/src/convert.rs b/wasm/lib/src/convert.rs index 641eb3538..af6ad029e 100644 --- a/wasm/lib/src/convert.rs +++ b/wasm/lib/src/convert.rs @@ -188,13 +188,13 @@ pub fn js_to_py(vm: &VirtualMachine, js_val: JsValue) -> PyObjectRef { u8_array.copy_to(&mut vec); vm.ctx.new_bytes(vec) } else { - let dict = vm.new_dict(); + let dict = vm.ctx.new_dict(); for pair in object_entries(&Object::from(js_val)) { let (key, val) = pair.expect("iteration over object to not fail"); let py_val = js_to_py(vm, val); dict.set_item(&vm.ctx, &String::from(js_sys::JsString::from(key)), py_val); } - dict + dict.into_object() } } else if js_val.is_function() { let func = js_sys::Function::from(js_val);