mod array_emitter; mod ast_emitter; mod block_emitter; mod compilation_info; mod control_structures; mod dis; mod emitter; mod emitter_scope; mod expression_emitter; mod function_declaration_emitter; mod object_emitter; mod reference_op_emitter; mod script_emitter; extern crate jsparagus_ast as ast; extern crate jsparagus_scope as scope; extern crate jsparagus_stencil as stencil; pub use crate::emitter::{EmitError, EmitOptions}; pub use dis::dis; use crate::compilation_info::CompilationInfo; use ast::source_atom_set::SourceAtomSet; use ast::source_slice_list::SourceSliceList; use scope::{ScopeBuildError, ScopePassResult}; use stencil::result::EmitResult; pub fn emit<'alloc>( ast: &'alloc ast::types::Program<'alloc>, options: &EmitOptions, atoms: SourceAtomSet<'alloc>, slices: SourceSliceList<'alloc>, ) -> Result, EmitError> { let ScopePassResult { scope_data_map, function_declarations, function_stencil_indices, function_declaration_properties, scripts, error, } = scope::generate_scope_data(ast); // Error case for scope analysis will be removed once all syntax is // supported. Use field instead of Result type here for simplicity. match error { Some(ScopeBuildError::NotImplemented(s)) => { return Err(EmitError::NotImplemented(s)); } None => {} } let compilation_info = CompilationInfo::new( atoms, slices, scope_data_map, function_declarations, function_stencil_indices, function_declaration_properties, scripts, ); ast_emitter::emit_program(ast, options, compilation_info) } #[cfg(test)] mod tests { extern crate jsparagus_parser as parser; use super::{emit, EmitOptions}; use crate::dis::*; use ast::source_atom_set::SourceAtomSet; use ast::source_slice_list::SourceSliceList; use bumpalo::Bump; use parser::{parse_script, ParseOptions}; use std::cell::RefCell; use std::convert::TryInto; use std::rc::Rc; use stencil::opcode::*; use stencil::script::SourceExtent; fn bytecode(source: &str) -> Vec { let alloc = &Bump::new(); let parse_options = ParseOptions::new(); let atoms = Rc::new(RefCell::new(SourceAtomSet::new())); let slices = Rc::new(RefCell::new(SourceSliceList::new())); let source_len = source.len(); let parse_result = parse_script(alloc, source, &parse_options, atoms.clone(), slices.clone()) .expect("Failed to parse"); // println!("{:?}", parse_result); let extent = SourceExtent::top_level_script(source_len.try_into().unwrap(), 1, 0); let emit_options = EmitOptions::new(extent); let result = emit( alloc.alloc(ast::types::Program::Script(parse_result.unbox())), &emit_options, atoms.replace(SourceAtomSet::new_uninitialized()), slices.replace(SourceSliceList::new()), ) .expect("Should work!"); let script_data_index: usize = result.scripts[0] .immutable_script_data .expect("Top level script should have ImmutableScriptData") .into(); let script_data = &result.script_data_list[script_data_index]; let bytecode = &script_data.bytecode; println!("{}", dis(&bytecode)); bytecode.to_vec() } #[test] fn it_works() { assert_eq!( bytecode("2 + 2"), vec![ Opcode::Int8 as u8, 2, Opcode::Int8 as u8, 2, Opcode::Add as u8, Opcode::SetRval as u8, Opcode::RetRval as u8, ] ) } #[test] fn dis_call() { assert_eq!( bytecode("dis()"), vec![ Opcode::GetGName as u8, 1, 0, 0, 0, Opcode::Undefined as u8, Opcode::Call as u8, 0, 0, Opcode::SetRval as u8, Opcode::RetRval as u8, ] ) } #[test] fn literals() { assert_eq!( bytecode("true"), vec![ Opcode::True as u8, Opcode::SetRval as u8, Opcode::RetRval as u8, ] ); assert_eq!( bytecode("false"), vec![ Opcode::False as u8, Opcode::SetRval as u8, Opcode::RetRval as u8, ] ); //assert_eq!( // bytecode("'hello world'"), // vec![ // Opcode::String as u8, 0, 0, 0, 0, // Opcode::SetRval as u8, // Opcode::RetRval as u8, // ] //); } }