diff --git a/porcelain/src/lib.rs b/porcelain/src/lib.rs index 8308cae..e593188 100644 --- a/porcelain/src/lib.rs +++ b/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/src/compile.rs b/src/compile.rs index 3e50e0e..6297c26 100644 --- a/src/compile.rs +++ b/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/src/mode.rs b/src/mode.rs index f926309..b56f226 100644 --- a/src/mode.rs +++ b/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),