diff --git a/compiler/porcelain/src/lib.rs b/compiler/porcelain/src/lib.rs index 8308cae0a..e593188ab 100644 --- a/compiler/porcelain/src/lib.rs +++ b/compiler/porcelain/src/lib.rs @@ -76,12 +76,12 @@ pub fn compile( source_path: String, opts: CompileOpts, ) -> Result { - let mode = match mode { + let parser_mode = match mode { compile::Mode::Exec => parser::Mode::Module, compile::Mode::Eval => parser::Mode::Expression, - compile::Mode::Single => parser::Mode::Interactive, + compile::Mode::Single | compile::Mode::BlockExpr => parser::Mode::Interactive, }; - let mut ast = match parser::parse(source, mode) { + let mut ast = match parser::parse(source, parser_mode) { Ok(x) => x, Err(e) => return Err(CompileError::from_parse(e, source, source_path)), }; @@ -90,7 +90,8 @@ pub fn compile( .fold_mod(ast) .unwrap_or_else(|e| match e {}); } - compile::compile_top(&ast, source_path, opts).map_err(|e| CompileError::from_compile(e, source)) + compile::compile_top(&ast, source_path, mode, opts) + .map_err(|e| CompileError::from_compile(e, source)) } pub fn compile_symtable( @@ -107,7 +108,7 @@ pub fn compile_symtable( }; } let res = match mode { - compile::Mode::Exec | compile::Mode::Single => { + compile::Mode::Exec | compile::Mode::Single | compile::Mode::BlockExpr => { let ast = try_parse!(parser::parse_program(source)); symboltable::make_symbol_table(&ast) } diff --git a/compiler/src/compile.rs b/compiler/src/compile.rs index 3e50e0eca..6297c26c5 100644 --- a/compiler/src/compile.rs +++ b/compiler/src/compile.rs @@ -104,11 +104,16 @@ impl CompileContext { pub fn compile_top( ast: &ast::Mod, source_path: String, + mode: Mode, opts: CompileOpts, ) -> CompileResult { match ast { ast::Mod::Module { body, .. } => compile_program(body, source_path, opts), - ast::Mod::Interactive { body } => compile_program_single(body, source_path, opts), + ast::Mod::Interactive { body } => match mode { + Mode::Single => compile_program_single(body, source_path, opts), + Mode::BlockExpr => compile_block_expression(body, source_path, opts), + _ => unreachable!("only Single and BlockExpr parsed to Interactive"), + }, ast::Mod::Expression { body } => compile_expression(body, source_path, opts), ast::Mod::FunctionType { .. } => panic!("can't compile a FunctionType"), } @@ -164,6 +169,20 @@ pub fn compile_program_single( ) } +pub fn compile_block_expression( + ast: &[ast::Stmt], + source_path: String, + opts: CompileOpts, +) -> CompileResult { + compile_impl( + ast, + source_path, + opts, + make_symbol_table, + Compiler::compile_block_expr, + ) +} + pub fn compile_expression( ast: &ast::Expr, source_path: String, @@ -370,6 +389,35 @@ impl Compiler { Ok(()) } + fn compile_block_expr( + &mut self, + body: &[ast::Stmt], + symbol_table: SymbolTable, + ) -> CompileResult<()> { + self.symbol_table_stack.push(symbol_table); + + self.compile_statements(body)?; + + if let Some(last_statement) = body.last() { + match last_statement.node { + ast::StmtKind::Expr { .. } => { + self.current_block().instructions.pop(); // pop Instruction::Pop + } + ast::StmtKind::FunctionDef { .. } + | ast::StmtKind::AsyncFunctionDef { .. } + | ast::StmtKind::ClassDef { .. } => { + let store_inst = self.current_block().instructions.pop().unwrap(); // pop Instruction::Store + self.emit(Instruction::Duplicate); + self.current_block().instructions.push(store_inst); + } + _ => self.emit_constant(ConstantData::None), + } + } + self.emit(Instruction::ReturnValue); + + Ok(()) + } + // Compile statement in eval mode: fn compile_eval( &mut self, diff --git a/compiler/src/mode.rs b/compiler/src/mode.rs index f926309af..b56f226a8 100644 --- a/compiler/src/mode.rs +++ b/compiler/src/mode.rs @@ -3,10 +3,13 @@ pub enum Mode { Exec, Eval, Single, + BlockExpr, } impl std::str::FromStr for Mode { type Err = ModeParseError; + + // To support `builtins.compile()` `mode` argument fn from_str(s: &str) -> Result { match s { "exec" => Ok(Mode::Exec), diff --git a/vm/src/stdlib/ast.rs b/vm/src/stdlib/ast.rs index 329bbd7a6..3655f5637 100644 --- a/vm/src/stdlib/ast.rs +++ b/vm/src/stdlib/ast.rs @@ -269,13 +269,14 @@ pub(crate) fn compile( vm: &VirtualMachine, object: PyObjectRef, filename: &str, - _mode: compile::Mode, + mode: compile::Mode, ) -> PyResult { let opts = vm.compile_opts(); let ast = Node::ast_from_object(vm, object)?; - let code = rustpython_compiler_core::compile::compile_top(&ast, filename.to_owned(), opts) - // TODO: use vm.new_syntax_error() - .map_err(|err| vm.new_value_error(err.to_string()))?; + let code = + rustpython_compiler_core::compile::compile_top(&ast, filename.to_owned(), mode, opts) + // TODO: use vm.new_syntax_error() + .map_err(|err| vm.new_value_error(err.to_string()))?; Ok(vm.ctx.new_code(code).into()) } diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index b3f125bc7..c1e438695 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -767,6 +767,18 @@ impl VirtualMachine { self.run_code_obj(code_obj, scope) } + pub fn run_block_expr(&self, scope: Scope, source: &str) -> PyResult { + let code_obj = self + .compile( + source, + crate::compile::Mode::BlockExpr, + "".to_owned(), + ) + .map_err(|err| self.new_syntax_error(&err))?; + // trace!("Code object: {:?}", code_obj.borrow()); + self.run_code_obj(code_obj, scope) + } + pub fn run_module(&self, module: &str) -> PyResult<()> { let runpy = self.import("runpy", None, 0)?; let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?;