diff --git a/src/behavior/builder_visitor.rs b/src/behavior/builder_visitor.rs index e3b52203..daffb4b3 100644 --- a/src/behavior/builder_visitor.rs +++ b/src/behavior/builder_visitor.rs @@ -11,12 +11,18 @@ use crate::behavior::tree::DecoratorType::{HasAltCall, PredIs}; use crate::common::error::ErrorGen; use crate::parser::types::{Global, ProvidedFunctionality}; -pub type SimpleAST = HashMap>>>>; +pub struct SimpleAST { + pub global_stmts: Vec, + pub probes: HashMap>>>>, +} pub fn build_behavior_tree(ast: &Whamm, err: &mut ErrorGen) -> (BehaviorTree, SimpleAST) { let mut visitor = BehaviorTreeBuilder { tree: BehaviorTree::new(), - ast: HashMap::new(), + ast: SimpleAST { + global_stmts: vec![], + probes: HashMap::new() + }, err, context_name: "".to_string(), curr_provider_name: "".to_string(), @@ -25,7 +31,7 @@ pub fn build_behavior_tree(ast: &Whamm, err: &mut ErrorGen) -> (BehaviorTree, Si }; visitor.visit_whamm(ast); - debug!("{:#?}", visitor.ast); + debug!("{:#?}", visitor.ast.probes); (visitor.tree, visitor.ast) } @@ -45,14 +51,14 @@ impl BehaviorTreeBuilder<'_> { // ======= fn add_provider_to_ast(&mut self, provider_name: String) { - if !self.ast.contains_key(&provider_name) { - self.ast.insert(provider_name.clone(), HashMap::new()); + if !self.ast.probes.contains_key(&provider_name) { + self.ast.probes.insert(provider_name.clone(), HashMap::new()); } self.curr_provider_name = provider_name; } fn add_package_to_ast(&mut self, package_name: String) { - if let Some(provider) = self.ast.get_mut(&self.curr_provider_name) { + if let Some(provider) = self.ast.probes.get_mut(&self.curr_provider_name) { if !provider.contains_key(&package_name) { provider.insert(package_name.clone(), HashMap::new()); } @@ -63,7 +69,7 @@ impl BehaviorTreeBuilder<'_> { } fn add_event_to_ast(&mut self, event_name: String) { - if let Some(provider) = self.ast.get_mut(&self.curr_provider_name) { + if let Some(provider) = self.ast.probes.get_mut(&self.curr_provider_name) { if let Some(package) = provider.get_mut(&self.curr_package_name) { if !package.contains_key(&event_name) { package.insert(event_name.clone(), HashMap::new()); @@ -76,7 +82,7 @@ impl BehaviorTreeBuilder<'_> { } fn add_probe_to_ast(&mut self, probe: &Probe) { - if let Some(provider) = self.ast.get_mut(&self.curr_provider_name) { + if let Some(provider) = self.ast.probes.get_mut(&self.curr_provider_name) { if let Some(package) = provider.get_mut(&self.curr_package_name) { if let Some(event) = package.get_mut(&self.curr_event_name) { if let Some(probes) = event.get_mut(&probe.mode) { @@ -97,23 +103,6 @@ impl BehaviorTreeBuilder<'_> { // = BehaviorTree = // ================ - fn visit_globals(&mut self, globals: &HashMap) { - if globals.len() > 0 { - self.tree.sequence(self.err); - - // visit globals - for (_name, global) in globals.iter() { - if global.is_comp_provided { - if let Expr::VarId { name, ..} = &global.var_name { - self.tree.define(self.context_name.clone(), - name.clone(), self.err); - } - } - } - self.tree.exit_sequence(self.err); - } - } - fn visit_provided_globals(&mut self, globals: &HashMap) { if globals.len() > 0 { self.tree.sequence(self.err); @@ -318,8 +307,12 @@ impl WhammVisitor<()> for BehaviorTreeBuilder<'_> { self.tree.enter_scope(self.context_name.clone(), script.name.clone(), self.err); - // visit globals - self.visit_globals(&script.globals); + // NOTE: visit_globals() is no longer needed since initializing user-defined globals is done + // in the init_generator (which doesn't traverse the behavior tree) + // RATHER, we process and emit the statements that do anything with the global vars + // (including declarations since that is an initialization action) + self.ast.global_stmts = script.global_stmts.to_owned(); + self.tree.emit_global_stmts(self.err); script.providers.iter().for_each(| (_name, provider) | { self.visit_provider(provider) @@ -356,7 +349,6 @@ impl WhammVisitor<()> for BehaviorTreeBuilder<'_> { fn visit_package(&mut self, package: &Package) -> () { trace!("Entering: BehaviorTreeBuilder::visit_package"); self.context_name += &format!(":{}", package.name.clone()); - self.add_package_to_ast(package.name.clone()); if self.is_in_context(r"whamm:script([0-9]+):wasm:bytecode") { self.visit_bytecode_package(package); @@ -368,6 +360,17 @@ impl WhammVisitor<()> for BehaviorTreeBuilder<'_> { } }; + // Handle AST separately since we don't visit every package + self.add_package_to_ast(package.name.clone()); + package.events.iter().for_each(| (_name, event) | { + self.add_event_to_ast(event.name.clone()); + + // Handle AST separately since we don't visit every probe + event.probe_map.iter().for_each(| (_mode, probe_list) | { + probe_list.iter().for_each(|probe| self.add_probe_to_ast(probe)); + }); + }); + trace!("Exiting: BehaviorTreeBuilder::visit_package"); // Remove this package from `context_name` self.context_name = self.context_name[..self.context_name.rfind(":").unwrap()].to_string(); diff --git a/src/behavior/tree.rs b/src/behavior/tree.rs index 1f769193..e1a2722d 100644 --- a/src/behavior/tree.rs +++ b/src/behavior/tree.rs @@ -208,6 +208,16 @@ impl BehaviorTree { self } + pub fn emit_global_stmts(&mut self, err: &mut ErrorGen) -> &mut Self { + let id = self.nodes.len(); + self.put_child(Node::Action { + id, + parent: self.curr, + ty: ActionType::EmitGlobalStmts + }, err); + self + } + pub fn emit_body(&mut self, err: &mut ErrorGen) -> &mut Self { let id = self.nodes.len(); self.put_child(Node::Action { @@ -461,6 +471,7 @@ pub enum ActionType { context: String, var_name: String }, + EmitGlobalStmts, EmitPred, Reset, EmitBody, @@ -589,6 +600,7 @@ pub trait BehaviorVisitor { ActionType::EnterScope {..} => self.visit_enter_scope(node), ActionType::ExitScope {..} => self.visit_exit_scope(node), ActionType::Define {..} => self.visit_define(node), + ActionType::EmitGlobalStmts {..} => self.visit_emit_global_stmts(node), ActionType::EmitPred {..} => self.visit_emit_pred(node), ActionType::Reset {..} => self.visit_reset(node), ActionType::EmitBody {..} => self.visit_emit_body(node), @@ -604,6 +616,7 @@ pub trait BehaviorVisitor { fn visit_enter_scope(&mut self, node: &Node) -> T; fn visit_exit_scope(&mut self, node: &Node) -> T; fn visit_define(&mut self, node: &Node) -> T; + fn visit_emit_global_stmts(&mut self, node: &Node) -> T; fn visit_emit_pred(&mut self, node: &Node) -> T; fn visit_reset(&mut self, node: &Node) -> T; fn visit_emit_body(&mut self, node: &Node) -> T; diff --git a/src/behavior/visualize.rs b/src/behavior/visualize.rs index e4e0a56e..af32860c 100644 --- a/src/behavior/visualize.rs +++ b/src/behavior/visualize.rs @@ -358,6 +358,19 @@ impl BehaviorVisitor<()> for Visualizer<'_> { } } + fn visit_emit_global_stmts(&mut self, node: &TreeNode) -> () { + if let TreeNode::Action { id, ty, parent} = node { + if let ActionType::EmitGlobalStmts = ty { + self.emit_special_action_node(id, "EmitGlobalStmts"); + self.emit_edge(parent, id); + } else { + unreachable!() + } + } else { + unreachable!() + } + } + fn visit_emit_pred(&mut self, node: &TreeNode) -> () { if let TreeNode::Action { id, ty, parent} = node { if let ActionType::EmitPred = ty { diff --git a/src/cli.rs b/src/cli.rs index 3bd03cc3..2926c1db 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,12 +8,12 @@ pub struct WhammCli { // global_opts: GlobalOpts, #[command(subcommand)] - pub(crate) command: Cmd + pub command: Cmd } #[derive(Debug, Subcommand)] -pub(crate) enum Cmd { - // /// Generate shell completion +pub enum Cmd { + // /// Generate completion for shell // Completion { // /// Shell to generate completion for // #[arg(arg_enum)] diff --git a/src/common/error.rs b/src/common/error.rs index 2986b9fd..1515920f 100644 --- a/src/common/error.rs +++ b/src/common/error.rs @@ -6,7 +6,7 @@ use crate::common::terminal::{blue, red, white}; use log::error; use pest::error::{Error, LineColLocation}; use pest::error::ErrorVariant::ParsingError; -use crate::parser::types::Rule; +use crate::parser::types::{Location, Rule}; const ERR_UNDERLINE_CHAR: char = '^'; const INFO_UNDERLINE_CHAR: char = '-'; @@ -129,45 +129,98 @@ impl ErrorGen { self.add_error(err); } - pub fn duplicate_identifier_error(&mut self, fatal: bool, duplicated_id: String, err_line_col: LineColLocation, info_line_col: LineColLocation) { - let err = WhammError { - fatal, - ty: ErrorType::DuplicateIdentifierError { - duplicated_id: duplicated_id.clone() - }, - err_loc: Some(CodeLocation { + pub fn get_duplicate_identifier_error(fatal: bool, duplicated_id: String, err_line_col: Option, info_line_col: Option) -> WhammError { + let err_loc = if let Some(err_line_col) = err_line_col { + Some(CodeLocation { is_err: true, message: Some(format!("duplicate definitions for `{}`", duplicated_id)), line_col: err_line_col, line_str: None, line2_str: None - }), - info_loc: Some(CodeLocation { - is_err: true, + }) + } else { + None + }; + let info_loc = if let Some(info_line_col) = info_line_col { + Some(CodeLocation { + is_err: false, message: Some(format!("other definition for `{}`", duplicated_id)), line_col: info_line_col, line_str: None, line2_str: None - }), + }) + } else { + None }; + + WhammError { + fatal, + ty: ErrorType::DuplicateIdentifierError { + duplicated_id: duplicated_id.clone() + }, + err_loc, + info_loc, + } + } + + pub fn get_duplicate_identifier_error_from_loc(fatal: bool, duplicated_id: String, err_loc: &Option, info_loc: &Option) -> WhammError { + let err_loc = if let Some(err_loc) = err_loc { + Some(err_loc.line_col.clone()) + } else { + None + }; + let info_loc = if let Some(info_loc) = info_loc { + Some(info_loc.line_col.clone()) + } else { + None + }; + Self::get_duplicate_identifier_error(fatal, duplicated_id, err_loc, info_loc) + } + + pub fn duplicate_identifier_error(&mut self, fatal: bool, duplicated_id: String, err_line_col: Option, info_line_col: Option) { + let err = Self::get_duplicate_identifier_error(fatal, duplicated_id, err_line_col, info_line_col); self.add_error(err); } - pub fn type_check_error(&mut self, fatal: bool, message: String, line_col: LineColLocation) { - let err = WhammError { + pub fn get_type_check_error(fatal: bool, message: String, loc: &Option) -> WhammError { + let loc = if let Some(loc) = loc { + Some(CodeLocation { + is_err: false, + message: Some(message.clone()), + line_col: loc.clone(), + line_str: None, + line2_str: None + }) + } else { + None + }; + + WhammError { fatal, ty: ErrorType::TypeCheckError { message: message.clone() }, - err_loc: Some(CodeLocation { - is_err: true, - message: Some(message), - line_col, - line_str: None, - line2_str: None - }), + err_loc: loc, info_loc: None + } + } + + pub fn get_type_check_error_from_loc(fatal: bool, message: String, line_col: &Option) -> WhammError { + let loc = if let Some(loc) = line_col { + Some(loc.line_col.clone()) + } else { + None }; + Self::get_type_check_error(fatal, message, &loc) + } + + pub fn type_check_error(&mut self, fatal: bool, message: String, line_col: &Option) { + let err = Self::get_type_check_error(fatal, message, line_col); + self.add_error(err); + } + + pub fn type_check_error_from_loc(&mut self, fatal: bool, message: String, loc: &Option) { + let err = Self::get_type_check_error_from_loc(fatal, message, loc); self.add_error(err); } @@ -498,7 +551,7 @@ impl WhammError { print_empty(&spacing, &mut buffer); } else { // This error isn't tied to a specific code location - blue(false, format!(" --> "), &mut buffer); + blue(false, " --> ".to_string(), &mut buffer); blue(false, format!("{script_path}\n\n"), &mut buffer); } writer.print(&buffer).expect("Uh oh, something went wrong while printing to terminal"); diff --git a/src/common/terminal.rs b/src/common/terminal.rs index 3dbc48eb..321f9660 100644 --- a/src/common/terminal.rs +++ b/src/common/terminal.rs @@ -36,7 +36,10 @@ pub fn red(bold: bool, s: String, buffer: &mut Buffer) { pub fn white(bold: bool, s: String, buffer: &mut Buffer) { color(s, buffer, bold, false, Color::Rgb(193,193,193)) } -pub fn white_italics(bold: bool, s: String, buffer: &mut Buffer) { +pub fn grey(bold: bool, s: String, buffer: &mut Buffer) { + color(s, buffer, bold, false, Color::White) +} +pub fn grey_italics(bold: bool, s: String, buffer: &mut Buffer) { color(s, buffer, bold, true, Color::White) } pub fn yellow(bold: bool, s: String, buffer: &mut Buffer) { diff --git a/src/generator/emitters.rs b/src/generator/emitters.rs index f58b17f2..e4975ca8 100644 --- a/src/generator/emitters.rs +++ b/src/generator/emitters.rs @@ -2,6 +2,7 @@ use log::{debug, info}; use regex::Regex; use walrus::{ActiveData, ActiveDataLocation, DataKind, FunctionBuilder, FunctionId, FunctionKind, ImportedFunction, InstrSeqBuilder, LocalFunction, MemoryId, ModuleData, ValType}; +use walrus::InitExpr::RefNull; use walrus::ir::{BinaryOp, ExtendedLoad, Instr, InstrSeqId, LoadKind, MemArg}; use crate::common::error::{ErrorGen, WhammError}; use crate::generator::types::ExprFolder; @@ -46,6 +47,7 @@ pub trait Emitter { fn emit_alternate(&mut self) -> bool; /// Will configure the emitter to emit subsequent statements in the outer block of some branching logic fn finish_branch(&mut self) -> bool; + fn emit_global_stmts(&mut self, stmts: &mut Vec) -> Result; fn emit_body(&mut self, body: &mut Vec) -> Result; fn has_alt_call(&mut self) -> bool; // TODO -- remove need for this fn emit_alt_call(&mut self) -> Result; // TODO -- remove need for this @@ -66,89 +68,64 @@ pub trait Emitter { const UNEXPECTED_ERR_MSG: &str = "WasmRewritingEmitter: Looks like you've found a bug...please report this behavior!"; -fn emit_stmt(table: &mut SymbolTable, module_data: &mut ModuleData, stmt: &mut Statement, - instr_builder: &mut InstrSeqBuilder, metadata: &mut InsertionMetadata, index: &mut usize) -> Result { - let is_success = true; - return match stmt { - Statement::Assign { var_id, expr, .. } => { - let folded_expr = ExprFolder::fold_expr(expr, table); - if let Expr::Primitive { val, .. } = folded_expr { - // This is a constant, just save the value to the symbol table for later use - if let Expr::VarId { name, .. } = var_id { - let var_rec_id = match table.lookup(name) { - Some(rec_id) => rec_id.clone(), - _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} VarId '{name}' does not exist in this scope!")), None)); - } - }; - match table.get_record_mut(&var_rec_id) { - Some(Record::Var { value, .. }) => { - *value = Some(val); - Ok(true) - }, - Some(ty) => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)); - }, - None => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Variable symbol does not exist!")), None)); - } +fn data_type_to_val_type(ty: &DataType) -> ValType { + match ty { + DataType::I32 => ValType::I32, + DataType::Boolean => ValType::I32, + DataType::Null => unimplemented!(), + DataType::Str => unimplemented!(), + DataType::Tuple { .. } => unimplemented!(), + // the ID used to track this var in the lib + DataType::Map { .. } => ValType::I32 + } +} + +fn emit_set(table: &mut SymbolTable, var_id: &mut Expr, instr_builder: &mut InstrSeqBuilder, index: &mut usize) -> Result { + if let Expr::VarId { name, .. } = var_id { + let var_rec_id = match table.lookup(name) { + Some(rec_id) => rec_id.clone(), + _ => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + VarId '{name}' does not exist in this scope!")), None)); + } + }; + match table.get_record_mut(&var_rec_id) { + Some(Record::Var { addr, loc, .. }) => { + // this will be different based on if this is a global or local var + match addr { + Some(VarAddr::Global { addr }) => { + instr_builder.instr_at(*index, walrus::ir::GlobalSet { + global: addr.clone() + }); + // update index to point to what follows our insertions + *index += 1; } - } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Expected VarId.")), None)); - } - } else { - return match emit_expr(table, module_data, expr, instr_builder, metadata, index) { - Err(e) => Err(e), - Ok(_) => { - if let Expr::VarId { name, .. } = var_id { - let var_rec_id = match table.lookup(name) { - Some(rec_id) => rec_id.clone(), - _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} VarId '{name}' does not exist in this scope!")), None)); - } - }; - match table.get_record_mut(&var_rec_id) { - Some(Record::Var { addr, .. }) => { - // this will be different based on if this is a global or local var - match addr { - Some(VarAddr::Global { addr }) => { - instr_builder.instr_at(*index, walrus::ir::GlobalSet { - global: addr.clone() - }); - // update index to point to what follows our insertions - *index += 1; - } - Some(VarAddr::Local { addr }) => { - instr_builder.instr_at(*index, walrus::ir::LocalSet { - local: addr.clone() - }); - // update index to point to what follows our insertions - *index += 1; - }, - None => { - // TODO No address yet, let's make a new local variable - unimplemented!() - } - } - Ok(is_success) - }, - Some(ty) => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)); - }, - None => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Variable symbol does not exist!")), None)); - } - } - } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Expected VarId.")), None)); - } + Some(VarAddr::Local { addr }) => { + instr_builder.instr_at(*index, walrus::ir::LocalSet { + local: addr.clone() + }); + // update index to point to what follows our insertions + *index += 1; + }, + None => { + return Err(ErrorGen::get_type_check_error_from_loc(false, + format!("Variable assigned before declared: {}", name), loc)); } } + Ok(true) + }, + Some(ty) => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)); + }, + None => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Variable symbol does not exist!")), None)); } } - Statement::Expr { expr, .. } => { - emit_expr(table, module_data, expr, instr_builder, metadata, index) - } + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Expected VarId.")), None)); } } @@ -156,6 +133,10 @@ fn emit_expr(table: &mut SymbolTable, module_data: &mut ModuleData, expr: &mut E metadata: &mut InsertionMetadata, index: &mut usize) -> Result { let mut is_success = true; match expr { + Expr::Ternary { cond: _cond, conseq: _conseq, alt: _alt, ..} => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Ternary expressions should be handled before this point!")), None)); + } Expr::BinOp {lhs, op, rhs, ..} => { is_success &= emit_expr(table, module_data, lhs, instr_builder, metadata, index)?; is_success &= emit_expr(table, module_data, rhs, instr_builder, metadata, index)?; @@ -194,11 +175,13 @@ fn emit_expr(table: &mut SymbolTable, module_data: &mut ModuleData, expr: &mut E // update index to point to what follows our insertions *index += 1; } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} fn_target address not in symbol table, not emitted yet...")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + fn_target address not in symbol table, not emitted yet...")), None)); } } _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} fn_target not defined in symbol table!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + fn_target not defined in symbol table!")), None)); } } }, @@ -213,7 +196,8 @@ fn emit_expr(table: &mut SymbolTable, module_data: &mut ModuleData, expr: &mut E let var_rec_id = match table.lookup(&name) { Some(rec_id) => rec_id.clone(), _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} VarId '{}' does not exist in this scope!", name)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + VarId '{}' does not exist in this scope!", name)), None)); } }; return match table.get_record_mut(&var_rec_id) { @@ -235,16 +219,19 @@ fn emit_expr(table: &mut SymbolTable, module_data: &mut ModuleData, expr: &mut E *index += 1; }, None => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Variable does not exist in scope: {}", name)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Variable does not exist in scope: {}", name)), None)); } } Ok(true) }, Some(ty) => { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)) }, None => { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Variable symbol does not exist!")), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Variable symbol does not exist!")), None)) } } } @@ -377,8 +364,9 @@ fn emit_op(op: &Op, instr_builder: &mut InstrSeqBuilder, index: &mut usize) -> b } } -fn emit_value(table: &mut SymbolTable, module_data: &mut ModuleData, val: &mut Value, instr_builder: &mut InstrSeqBuilder, - metadata: &mut InsertionMetadata, index: &mut usize) -> Result { +fn emit_value(table: &mut SymbolTable, module_data: &mut ModuleData, val: &mut Value, + instr_builder: &mut InstrSeqBuilder, metadata: &mut InsertionMetadata, + index: &mut usize) -> Result { let mut is_success = true; match val { Value::Integer { val, .. } => { @@ -395,7 +383,7 @@ fn emit_value(table: &mut SymbolTable, module_data: &mut ModuleData, val: &mut V location: ActiveDataLocation::Absolute(metadata.curr_mem_offset.clone()) }), Vec::from(val.as_bytes())); - // save the memory addresses/lens so they can be used as appropriate + // save the memory addresses/lens, so they can be used as appropriate *addr = Some(( data_id, metadata.curr_mem_offset.clone(), @@ -424,7 +412,8 @@ fn emit_value(table: &mut SymbolTable, module_data: &mut ModuleData, val: &mut V } } Value::Boolean { val, .. } => { - // "In a boolean context, such as a br_if condition, any non-zero value is interpreted as true and 0 is interpreted as false." + // "In a boolean context, such as a br_if condition, any non-zero value is interpreted as true + // and 0 is interpreted as false." // https://github.com/sunfishcode/wasm-reference-manual/blob/master/WebAssembly.md#booleans if *val { // insert true (non-zero) @@ -532,8 +521,9 @@ impl InstrIter { debug!("Finished creating list of instructions to visit"); } - fn init_instr_locs(&mut self, instrs_of_interest: &Vec, app_wasm: &walrus::Module, func: &LocalFunction, func_id: &FunctionId, - func_name: Option, instr_seq_id: InstrSeqId) { + fn init_instr_locs(&mut self, instrs_of_interest: &Vec, app_wasm: &walrus::Module, + func: &LocalFunction, func_id: &FunctionId, func_name: Option, + instr_seq_id: InstrSeqId) { func.block(instr_seq_id) .iter() .enumerate() @@ -570,15 +560,19 @@ impl InstrIter { // visit nested blocks match instr { Instr::Block(block) => { - self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, func_name.clone(), block.seq); + self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, + func_name.clone(), block.seq); } Instr::Loop(_loop) => { - self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, func_name.clone(), _loop.seq); + self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, + func_name.clone(), _loop.seq); } Instr::IfElse(if_else, ..) => { println!("IfElse: {:#?}", if_else); - self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, func_name.clone(), if_else.consequent); - self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, func_name.clone(), if_else.alternative); + self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, + func_name.clone(), if_else.consequent); + self.init_instr_locs(instrs_of_interest, app_wasm, func, func_id, + func_name.clone(), if_else.alternative); } _ => { // do nothing extra for other instructions @@ -711,7 +705,8 @@ impl WasmRewritingEmitter { let rec_id = match self.table.lookup(&var_name) { Some(rec_id) => rec_id.clone(), _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} `{var_name}` symbol does not exist in this scope!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + `{var_name}` symbol does not exist in this scope!")), None)); } }; self.override_var_val(&rec_id, Some(Value::Str { @@ -736,7 +731,8 @@ impl WasmRewritingEmitter { let rec_id = match self.table.lookup(&var_name) { Some(rec_id) => rec_id.clone(), _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} `{var_name}` symbol does not exist in this scope!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + `{var_name}` symbol does not exist in this scope!")), None)); } }; self.override_var_val(&rec_id, Some(Value::Str { @@ -760,7 +756,8 @@ impl WasmRewritingEmitter { let rec_id = match self.table.lookup(&var_name) { Some(rec_id) => rec_id.clone(), _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} `{var_name}` symbol does not exist in this scope!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + `{var_name}` symbol does not exist in this scope!")), None)); } }; self.override_var_val(&rec_id, Some(Value::Str { @@ -777,7 +774,8 @@ impl WasmRewritingEmitter { return if context == "whamm" && f.name.name == "strcmp" { self.emit_whamm_strcmp_fn(f) } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Provided function, but could not find a context to provide the definition, context: {}", context)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Provided function, but could not find a context to provide the definition, context: {}", context)), None)); } } @@ -901,7 +899,8 @@ impl WasmRewritingEmitter { let rec_id = match self.table.lookup(&f.name.name) { Some(rec_id) => *rec_id, _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} strcmp fn symbol does not exist in this scope!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + `strcmp` fn symbol does not exist in this scope!")), None)); } }; @@ -910,12 +909,153 @@ impl WasmRewritingEmitter { *addr = Some(strcmp_id); Ok(true) } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Incorrect global variable record, expected Record::Var, found: {:?}", rec)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect global variable record, expected Record::Var, found: {:?}", rec)), None)); } } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Global variable symbol does not exist!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Global variable symbol does not exist!")), None)); }; } + + fn emit_decl_stmt(&mut self, stmt: &mut Statement) -> Result { + match stmt { + Statement::Decl {ty, var_id, ..} => { + // look up in symbol table + let mut addr = if let Expr::VarId { name, .. } = var_id { + let var_rec_id = match self.table.lookup(name) { + Some(rec_id) => rec_id.clone(), + None => { + // TODO -- add variables from body into symbol table + // (at this point, the verifier should have run to catch variable initialization without declaration) + self.table.put(name.clone(), Record::Var { + ty: ty.clone(), + name: name.clone(), + value: None, + addr: None, + loc: None + }) + } + }; + match self.table.get_record_mut(&var_rec_id) { + Some(Record::Var { addr, .. }) => { + addr + }, + Some(ty) => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)); + }, + None => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Variable symbol does not exist!")), None)); + } + } + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Expected VarId.")), None)); + }; + + match &mut addr { + Some(VarAddr::Global { addr: _addr }) => { + // The global should already exist, do any initial setup here! + match ty { + DataType::Map {key_ty: _key_ty, val_ty: _val_ty} => { + // initialize map global variable + // also update value at GID (probably need to set ID of map there) + unimplemented!() + } + _ => { + Ok(true) + } + } + }, + Some(VarAddr::Local { .. }) | None => { + // If the local already exists, it would be because the probe has been + // emitted at another bytecode location. Simply overwrite the previously saved + // address. + let walrus_ty = data_type_to_val_type(&ty); + let id = self.app_wasm.locals.add(walrus_ty); + *addr = Some(VarAddr::Local { + addr: id + }); + Ok(true) + } + } + } + _ => { + return Err(ErrorGen::get_unexpected_error(false, Some(format!("{UNEXPECTED_ERR_MSG} \ + Wrong statement type, should be `assign`")), None)); + } + } + } + + fn emit_assign_stmt(&mut self, stmt: &mut Statement) -> Result { + return match stmt { + Statement::Assign { var_id, expr, .. } => { + let folded_expr = ExprFolder::fold_expr(expr, &self.table); + match folded_expr { + Expr::Primitive {val, ..} => { + // This is a constant, just save the value to the symbol table for later use + if let Expr::VarId { name, .. } = var_id { + let var_rec_id = match self.table.lookup(name) { + Some(rec_id) => rec_id.clone(), + _ => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + VarId '{name}' does not exist in this scope!")), None)); + } + }; + match self.table.get_record_mut(&var_rec_id) { + Some(Record::Var { value, .. }) => { + *value = Some(val); + Ok(true) + }, + Some(ty) => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect variable record, expected Record::Var, found: {:?}", ty)), None)); + }, + None => { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Variable symbol does not exist!")), None)); + } + } + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Expected VarId.")), None)); + } + } + Expr::VarId {..} | Expr::BinOp {..} | Expr::Call {..} | Expr::Ternary {..} => { + // Anything else can be emitted as normal + return match self.emit_expr(expr) { + Err(e) => Err(e), + Ok(_) => { + if let Some(curr_loc) = self.instr_iter.curr_mut() { + if let Some(tracker) = &mut self.emitting_instr { + let func = self.app_wasm.funcs.get_mut(curr_loc.wasm_func_id).kind.unwrap_local_mut(); + let func_builder = func.builder_mut(); + let mut instr_builder = func_builder.instr_seq(tracker.curr_seq_id); + + // Emit the instruction that sets the var's value to the emitted expression + emit_set(&mut self.table, var_id, &mut instr_builder, &mut tracker.curr_idx) + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Something went wrong while emitting an instruction.")), None)); + } + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Something went wrong while emitting an instruction.")), None)); + } + } + } + + } + } + } + _ => { + return Err(ErrorGen::get_unexpected_error(false, Some(format!("{UNEXPECTED_ERR_MSG} \ + Wrong statement type, should be `assign`")), None)); + } + } + } } impl Emitter for WasmRewritingEmitter { @@ -996,9 +1136,10 @@ impl Emitter for WasmRewritingEmitter { curr_instr.instr_params = Some(func_info.params); } - return Ok(curr_instr.instr_params.as_ref().unwrap().len() > 0); + return Ok(curr_instr.instr_params.is_some() && curr_instr.instr_params.as_ref().unwrap().len() > 0); } - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Something went wrong when trying to access the current instruction.")), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Something went wrong when trying to access the current instruction.")), None)) } fn save_params(&mut self) -> bool { @@ -1028,7 +1169,7 @@ impl Emitter for WasmRewritingEmitter { // place in symbol table with var addr for future reference let arg_name = format!("arg{}", num); let id = self.table.put(arg_name.clone(), Record::Var { - ty: DataType::Integer, // we only support integers right now. + ty: DataType::I32, // we only support integers right now. name: arg_name.clone(), value: None, addr: Some(VarAddr::Local { @@ -1061,7 +1202,8 @@ impl Emitter for WasmRewritingEmitter { }); tracker.curr_idx += 1; } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Could not emit parameters, something went wrong...")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Could not emit parameters, something went wrong...")), None)); } } return Ok(true); @@ -1087,11 +1229,13 @@ impl Emitter for WasmRewritingEmitter { self.define_target_imp_module() } _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Current context `{}` does not provide definition for variable `{}`", context, var_name)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Current context `{}` does not provide definition for variable `{}`", context, var_name)), None)); } } } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Could not find a context to provide the definition, context: {}", context)), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Could not find a context to provide the definition, context: {}", context)), None)); }; } @@ -1100,21 +1244,39 @@ impl Emitter for WasmRewritingEmitter { true } fn emit_expr(&mut self, expr: &mut Expr) -> Result { - if let Some(curr_loc) = self.instr_iter.curr_mut() { - if let Some(tracker) = &mut self.emitting_instr { - let func = self.app_wasm.funcs.get_mut(curr_loc.wasm_func_id).kind.unwrap_local_mut(); - let func_builder = func.builder_mut(); - let mut instr_builder = func_builder.instr_seq(tracker.curr_seq_id); - - emit_expr(&mut self.table, &mut self.app_wasm.data, expr, - &mut instr_builder, &mut self.metadata, &mut tracker.curr_idx)?; - } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Something went wrong while emitting an instruction.")), None)); + let mut is_success = true; + match expr { + Expr::Ternary {cond, conseq, alt, ..} => { + is_success &= self.emit_if_else(); + is_success &= self.emit_condition(); + is_success &= self.emit_expr(cond)?; + is_success &= self.emit_consequent(); + is_success &= self.emit_expr(conseq)?; + is_success &= self.emit_alternate(); + is_success &= self.emit_expr(alt)?; + is_success &= self.finish_branch(); + }, + Expr::VarId {..} | Expr::BinOp {..} | Expr::Primitive {..} | Expr::Call {..} => { + // Anything else can be emitted as normal + if let Some(curr_loc) = self.instr_iter.curr_mut() { + if let Some(tracker) = &mut self.emitting_instr { + let func = self.app_wasm.funcs.get_mut(curr_loc.wasm_func_id).kind.unwrap_local_mut(); + let func_builder = func.builder_mut(); + let mut instr_builder = func_builder.instr_seq(tracker.curr_seq_id); + + is_success &= emit_expr(&mut self.table, &mut self.app_wasm.data, expr, + &mut instr_builder, &mut self.metadata, &mut tracker.curr_idx)?; + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Something went wrong while emitting an instruction.")), None)); + } + } else { + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Something went wrong while emitting an instruction.")), None)); + } } - } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Something went wrong while emitting an instruction.")), None)); } - return Ok(true); + return Ok(is_success); } fn emit_fn(&mut self, context: &str, f: &Fn) -> Result { // figure out if this is a provided fn. @@ -1122,7 +1284,8 @@ impl Emitter for WasmRewritingEmitter { return if self.fn_providing_contexts.contains(&context.to_string()) { self.emit_provided_fn(context, f) } else { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Provided fn, but could not find a context to provide the definition, context: {}", context)), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Provided fn, but could not find a context to provide the definition, context: {}", context)), None)) } } @@ -1136,26 +1299,35 @@ impl Emitter for WasmRewritingEmitter { unimplemented!(); } - fn emit_global(&mut self, name: String, _ty: DataType, _val: &Option) -> Result { + fn emit_global(&mut self, name: String, ty: DataType, _val: &Option) -> Result { let rec_id = match self.table.lookup(&name) { Some(rec_id) => rec_id.clone(), _ => { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Global variable symbol does not exist in this scope!")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Global variable symbol does not exist in this scope!")), None)); } // Ignore, continue to emit }; let rec = self.table.get_record_mut(&rec_id); return match rec { - Some(Record::Var { addr: _addr, .. }) => { + Some(Record::Var { ref mut addr, .. }) => { // emit global variable and set addr in symbol table - // only when we're supporting user-defined globals in script... - unimplemented!(); + // this is used for user-defined global vars in the script... + let walrus_ty = data_type_to_val_type(&ty); + let id = self.app_wasm.globals.add_local(walrus_ty.clone(), false, RefNull(walrus_ty)); + *addr = Some(VarAddr::Global { + addr: id + }); + + Ok(true) }, - Some(ty) => { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Incorrect global variable record, expected Record::Var, found: {:?}", ty)), None)) + Some(&mut ref ty) => { + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Incorrect global variable record, expected Record::Var, found: {:?}", ty)), None)) }, None => { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Global variable symbol does not exist!")), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Global variable symbol does not exist!")), None)) } } } @@ -1196,31 +1368,45 @@ impl Emitter for WasmRewritingEmitter { let func_builder = func.builder_mut(); let mut instr_builder = func_builder.instr_seq(tracker.curr_seq_id); + let mut outer_seq_id = None; + let mut outer_idx = None; + let mut then_seq_id = None; + let mut then_idx = None; + instr_builder.block_at( tracker.curr_idx, None, |outer_block| { - let outer_block_id = outer_block.id(); - // create new `index` var to store current index into the of the `then` instr sequence - let outer_block_idx = 0 as usize; - - // Add logic that will execute after the injected conditional to - // break out of the if block if it evaluates to true. - // If result of predicate equals 0, break out of the probe block. - // Will continue with the application code. + let outer_id = outer_block.id(); + outer_seq_id = Some(outer_id.clone()); + outer_idx = Some(0usize); + + // CONDITION SHOULD BE EMITTED HERE + + // If the block evaluates to true (any nonzero value), execute the body. + // If result of predicate equals 0, break out of the probe block + // to continue with the application code. outer_block .i32_const(0) .binop(BinaryOp::I32Eq) - .br_if(outer_block_id); + .br_if(outer_id); - // Leave block index at 0 to enable injecting conditional before the - // above instructions. + outer_block.block( + None, + |then| { + then_seq_id = Some(then.id()); + then_idx = Some(0usize); - // Save the block information for future reference - tracker.outer_seq_id = Some(outer_block_id); - tracker.outer_idx = Some(outer_block_idx); + // CONSEQUENT SHOULD BE EMITTED HERE + }); }); + // Save the block information for future reference + // leave outer_block_idx as 0 to enable injection of condition! + tracker.outer_seq_id = outer_seq_id; + tracker.outer_idx = outer_idx; + tracker.then_seq_id = then_seq_id; + tracker.then_idx = then_idx; tracker.curr_idx += 1; return true; } @@ -1248,16 +1434,16 @@ impl Emitter for WasmRewritingEmitter { None, |outer_block| { outer_seq_id = Some(outer_block.id()); - outer_idx = Some(0 as usize); + outer_idx = Some(0usize); outer_block.if_else( None, | then | { then_seq_id = Some(then.id()); - then_idx = Some(0 as usize); + then_idx = Some(0usize); }, |else_| { else_seq_id = Some(else_.id()); - else_idx = Some(0 as usize); + else_idx = Some(0usize); }, ); }); @@ -1335,6 +1521,40 @@ impl Emitter for WasmRewritingEmitter { } true } + fn emit_global_stmts(&mut self, stmts: &mut Vec) -> Result { + // NOTE: This should be done in the Module entrypoint + // https://docs.rs/walrus/latest/walrus/struct.Module.html + if self.app_wasm.start.is_none() { + // unimplemented!() + } + + if let Some(start_fid) = self.app_wasm.start { + if let FunctionKind::Local(local_func) = &self.app_wasm.funcs.get(start_fid).kind { + self.emitting_instr = Some(EmittingInstrTracker { + curr_seq_id: local_func.entry_block().clone(), + curr_idx: 0usize, + main_seq_id: local_func.entry_block().clone(), + main_idx: 0usize, + outer_seq_id: None, + outer_idx: None, + then_seq_id: None, + then_idx: None, + else_seq_id: None, + else_idx: None, + }) + } + } else { + // return Err(ErrorGen::get_unexpected_error(true, + // Some("This module has no configured entrypoint, \ + // enable to emit `script` with global state".to_string()), None)); + } + + for stmt in stmts.iter_mut() { + // iterate over statements and emit them + self.emit_stmt(stmt)?; + } + Ok(true) + } fn emit_body(&mut self, body: &mut Vec) -> Result { for stmt in body.iter_mut() { @@ -1399,7 +1619,8 @@ impl Emitter for WasmRewritingEmitter { tracker.curr_idx += 1; } else { - return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Could not inject alternate call to function, something went wrong...")), None)); + return Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Could not inject alternate call to function, something went wrong...")), None)); } } } @@ -1407,17 +1628,17 @@ impl Emitter for WasmRewritingEmitter { } fn emit_stmt(&mut self, stmt: &mut Statement) -> Result { - if let Some(curr_loc) = self.instr_iter.curr_mut() { - if let Some(tracker) = &mut self.emitting_instr { - let func = self.app_wasm.funcs.get_mut(curr_loc.wasm_func_id).kind.unwrap_local_mut(); - let func_builder = func.builder_mut(); - let mut instr_builder = func_builder.instr_seq(tracker.curr_seq_id); - - return emit_stmt(&mut self.table, &mut self.app_wasm.data, stmt, - &mut instr_builder, &mut self.metadata, &mut tracker.curr_idx); + return match stmt { + Statement::Decl {..} => { + self.emit_decl_stmt(stmt) + }, + Statement::Assign {..} => { + self.emit_assign_stmt(stmt) } - } - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Something went wrong while emitting a statement.")), stmt.line_col())) + Statement::Expr { expr, .. } => { + return self.emit_expr(expr) + } + }; } fn dump_to_file(&mut self, output_wasm_path: String) -> Result { @@ -1426,7 +1647,8 @@ impl Emitter for WasmRewritingEmitter { Ok(true) }, Err(err) => { - Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} Failed to dump instrumented wasm to {} from error: {}", &output_wasm_path, err)), None)) + Err(ErrorGen::get_unexpected_error(true, Some(format!("{UNEXPECTED_ERR_MSG} \ + Failed to dump instrumented wasm to {} from error: {}", &output_wasm_path, err)), None)) }, } } diff --git a/src/generator/init_generator.rs b/src/generator/init_generator.rs index c6aca36e..b7c4e585 100644 --- a/src/generator/init_generator.rs +++ b/src/generator/init_generator.rs @@ -22,6 +22,8 @@ pub struct InitGenerator<'a> { } impl InitGenerator<'_> { pub fn run(&mut self, whamm: &mut Whamm) -> bool { + // Reset the symbol table in the emitter just in case + self.emitter.reset_children(); // Generate globals and fns defined by `whamm` (this should modify the app_wasm) self.visit_whamm(whamm) } diff --git a/src/generator/instr_generator.rs b/src/generator/instr_generator.rs index ab8d29f1..1e2f9f03 100644 --- a/src/generator/instr_generator.rs +++ b/src/generator/instr_generator.rs @@ -524,6 +524,27 @@ impl BehaviorVisitor for InstrGenerator<'_, '_> { is_success } + fn visit_emit_global_stmts(&mut self, node: &Node) -> bool { + let mut is_success = true; + if let Node::Action { ty, ..} = node { + if let ActionType::EmitGlobalStmts = ty { + // NOTE -- this WILL NOT WORK for dfinity or microservice applications...they are stateless + // will need to instrument ALL entrypoints for that to work :/ + if !self.ast.global_stmts.is_empty() { + match self.emitter.emit_global_stmts(&mut self.ast.global_stmts) { + Err(e) => self.err.add_error(e), + Ok(res) => is_success &= res, + } + } + } else { + unreachable!() + } + } else { + unreachable!() + } + is_success + } + fn visit_emit_pred(&mut self, node: &Node) -> bool { let mut is_success = true; if let Node::Action {ty, ..} = node { @@ -645,7 +666,7 @@ impl BehaviorVisitor for InstrGenerator<'_, '_> { fn get_probes_from_ast<'a>(ast: &'a SimpleAST, curr_provider_name: &String, curr_package_name: &String, curr_event_name: &String, name: &String) -> &'a Vec { - if let Some(provider) = ast.get(curr_provider_name) { + if let Some(provider) = ast.probes.get(curr_provider_name) { if let Some(package) = provider.get(curr_package_name) { if let Some(event) = package.get(curr_event_name) { if let Some(probes) = event.get(name) { diff --git a/src/generator/tests.rs b/src/generator/tests.rs index 23f6027f..3e9caf71 100644 --- a/src/generator/tests.rs +++ b/src/generator/tests.rs @@ -174,8 +174,8 @@ fn assert_simplified_predicate(pred: &Expr) { fn basic_run(script: &str, err: &mut ErrorGen) { match tests::get_ast(script, err) { - Some(whamm) => { - let mut table = verifier::build_symbol_table(&whamm, err); + Some(mut whamm) => { + let mut table = verifier::build_symbol_table(&mut whamm, err); table.reset(); let pred = get_pred(&whamm); @@ -265,8 +265,8 @@ wasm::call:alt / let mut err = ErrorGen::new("".to_string(), "".to_string(), 0); match tests::get_ast(script, &mut err) { - Some(whamm) => { - let mut table = verifier::build_symbol_table(&whamm, &mut err); + Some(mut whamm) => { + let mut table = verifier::build_symbol_table(&mut whamm, &mut err); table.reset(); let pred = get_pred(&whamm); diff --git a/src/generator/types.rs b/src/generator/types.rs index d61d8a7c..0e64c7b1 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -10,6 +10,9 @@ pub struct ExprFolder; impl ExprFolder { pub fn fold_expr(expr: &Expr, table: &SymbolTable) -> Expr { match *expr { + Expr::Ternary { .. } => { + ExprFolder::fold_ternary(expr, table) + } Expr::BinOp { .. } => { ExprFolder::fold_binop(expr, table) } @@ -192,7 +195,7 @@ impl ExprFolder { _ => {} } - // Cannot fold any more + // Cannot fold anymore binop.clone() } @@ -269,35 +272,35 @@ impl ExprFolder { }), Op::Add => Some(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: lhs_int + rhs_int, }, loc: None }), Op::Subtract => Some(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: lhs_int - rhs_int, }, loc: None }), Op::Multiply => Some(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: lhs_int * rhs_int, }, loc: None }), Op::Divide => Some(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: lhs_int / rhs_int, }, loc: None }), Op::Modulo => Some(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: lhs_int % rhs_int, }, loc: None @@ -334,6 +337,10 @@ impl ExprFolder { None } + fn fold_ternary(_ternary: &Expr, _table: &SymbolTable) -> Expr { + todo!() + } + fn fold_call(call: &Expr, _table: &SymbolTable) -> Expr { call.clone() } diff --git a/src/main.rs b/src/main.rs index cf859e46..e5b84f6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -86,7 +86,7 @@ fn run_instr(app_wasm_path: String, script_path: String, output_wasm_path: Strin // Process the script let mut whamm = get_script_ast(&script_path, &mut err); - let symbol_table = get_symbol_table(&whamm, run_verifier, &mut err); + let symbol_table = get_symbol_table(&mut whamm, run_verifier, &mut err); let (behavior_tree, simple_ast) = build_behavior(&whamm, &mut err); // If there were any errors encountered, report and exit! @@ -187,11 +187,13 @@ fn run_vis_script(script_path: String, run_verifier: bool, output_path: String) // Set up error reporting mechanism let mut err = ErrorGen::new(script_path.clone(), "".to_string(), MAX_ERRORS); - let whamm = get_script_ast(&script_path, &mut err); - verify_ast(&whamm, run_verifier, &mut err); + let mut whamm = get_script_ast(&script_path, &mut err); + // building the symbol table is necessary since it does some minor manipulations of the AST + // (adds declared globals to the script AST node) + let _symbol_table = get_symbol_table(&mut whamm, run_verifier, &mut err); let (behavior_tree, ..) = build_behavior(&whamm, &mut err); - // if has any errors, should report and exit! + // if there are any errors, should report and exit! err.check_has_errors(); let path = match get_pb(&PathBuf::from(output_path.clone())) { @@ -218,8 +220,8 @@ fn run_vis_script(script_path: String, run_verifier: bool, output_path: String) exit(0); } -fn get_symbol_table(ast: &Whamm, run_verifier: bool, err: &mut ErrorGen) -> SymbolTable { - let st = build_symbol_table(&ast, err); +fn get_symbol_table(ast: &mut Whamm, run_verifier: bool, err: &mut ErrorGen) -> SymbolTable { + let st = build_symbol_table(ast, err); err.check_too_many(); verify_ast(ast, run_verifier, err); st diff --git a/src/parser/print_visitor.rs b/src/parser/print_visitor.rs index c691474d..6bb69651 100644 --- a/src/parser/print_visitor.rs +++ b/src/parser/print_visitor.rs @@ -344,6 +344,9 @@ impl WhammVisitor for AsStrVisitor { fn visit_stmt(&mut self, stmt: &Statement) -> String { match stmt { + Statement::Decl {ty, var_id, ..} => { + format!("{} {}", self.visit_datatype(ty), self.visit_expr(var_id)) + } Statement::Assign {var_id, expr, ..} => { format!("{} = {}", self.visit_expr(var_id), self.visit_expr(expr)) }, @@ -355,6 +358,15 @@ impl WhammVisitor for AsStrVisitor { fn visit_expr(&mut self, expr: &Expr) -> String { match expr { + Expr::Ternary {cond, conseq, alt, ..} => { + let mut s = "".to_string(); + s += &format!("{} ? {} : {}", + self.visit_expr(cond), + self.visit_expr(conseq), + self.visit_expr(alt) + ); + s + } Expr::BinOp {lhs, op, rhs, ..} => { let mut s = "".to_string(); s += &format!("{} {} {}", @@ -407,9 +419,9 @@ impl WhammVisitor for AsStrVisitor { fn visit_datatype(&mut self, datatype: &DataType) -> String { match datatype { - DataType::Integer => { + DataType::I32 => { "int".to_string() - }, + } DataType::Boolean => { "bool".to_string() }, @@ -422,6 +434,9 @@ impl WhammVisitor for AsStrVisitor { DataType::Tuple {..} => { "tuple".to_string() }, + DataType::Map {..} => { + "map".to_string() + }, } } diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 092c5cd6..95de5ee0 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -16,6 +16,13 @@ pub fn setup_logger() { } const VALID_SCRIPTS: &'static [&'static str] = &[ + // Ternary + r#" +wasm:bytecode:br:before { + index = i ? 1 : 0; +} + "#, + // Variations of PROBE_SPEC "BEGIN { }", "END { }", @@ -63,11 +70,22 @@ wasm::call:alt / } "#, + // globals + r#" +map count; +BEGIN { } + "#, + r#" +map count; +count = 0; +BEGIN { } + "#, + // Statements r#" - wasm:bytecode:br:before { - i = 0; - } +wasm:bytecode:br:before { + i = 0; +} "#, // Comments @@ -88,6 +106,11 @@ wasm:bytecode:br:before { ]; const INVALID_SCRIPTS: &'static [&'static str] = &[ + // globals + r#" +map count; + "#, + // Variations of PROBE_SPEC "wasm:bytecode:call:alt: { }", "wasm:bytecode:call:alt", @@ -252,7 +275,7 @@ wasm::call:alt / assert_eq!(1, provider.packages.len()); let package = provider.packages.get("bytecode").unwrap(); assert_eq!("bytecode", package.name); - assert_eq!(0, package.globals.len()); + assert_eq!(2, package.globals.len()); assert_eq!(0, package.fns.len()); assert_eq!(1, package.events.len()); diff --git a/src/parser/types.rs b/src/parser/types.rs index 00f5e39d..e7b46492 100644 --- a/src/parser/types.rs +++ b/src/parser/types.rs @@ -7,7 +7,7 @@ use pest_derive::Parser; use pest::pratt_parser::PrattParser; use termcolor::BufferWriter; use walrus::DataId; -use crate::common::terminal::{green, long_line, magenta, magenta_italics, white, white_italics, yellow}; +use crate::common::terminal::{green, long_line, magenta, magenta_italics, white, grey_italics, yellow}; use crate::common::error::{ErrorGen, WhammError}; #[derive(Parser)] @@ -80,18 +80,22 @@ impl Location { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum DataType { - Integer, + I32, Boolean, Null, Str, Tuple { ty_info: Option>> + }, + Map { + key_ty: Box, + val_ty: Box } } impl DataType { pub fn print(&self, buffer: &mut Buffer) { match self { - DataType::Integer => { + DataType::I32 => { yellow(true, "int".to_string(), buffer); }, DataType::Boolean => { @@ -116,6 +120,14 @@ impl DataType { } } white(true, ")".to_string(), buffer); + }, + DataType::Map {key_ty, val_ty} => { + yellow(true, "map".to_string(), buffer); + white(true, "<".to_string(), buffer); + key_ty.print(buffer); + white(true, ", ".to_string(), buffer); + val_ty.print(buffer); + white(true, ">".to_string(), buffer); } } } @@ -152,6 +164,12 @@ pub enum Value { // Statements #[derive(Clone, Debug)] pub enum Statement { + Decl { + ty: DataType, + var_id: Expr, // should be VarId + loc: Option + }, + Assign { var_id: Expr, // Should be VarId expr: Expr, @@ -171,6 +189,7 @@ pub enum Statement { impl Statement { pub fn loc(&self) -> &Option { match self { + Statement::Decl {loc, ..} | Statement::Assign {loc, ..} | Statement::Expr {loc, ..} => { loc @@ -187,7 +206,7 @@ impl Statement { Self::Expr { expr: Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val: 0, }, loc: None @@ -199,6 +218,12 @@ impl Statement { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Expr { + Ternary { + cond: Box, + conseq: Box, + alt: Box, + loc: Option + }, BinOp { // Type is based on the outermost `op` (if arithmetic op, also based on types of lhs/rhs due to doubles) lhs: Box, op: Op, @@ -223,6 +248,7 @@ pub enum Expr { impl Expr { pub fn loc(&self) -> &Option { match self { + Expr::Ternary {loc, ..} | Expr::BinOp {loc, ..} | Expr::Call {loc, ..} | Expr::VarId {loc, ..} | @@ -305,7 +331,7 @@ fn print_global_vars(tabs: &mut usize, globals: &HashMap, buf for (info, f) in functions.iter() { green(true, format!("{}", " ".repeat(*tabs * 4)), buffer); f.print(buffer); - green(true, format!("\n"), buffer); + green(true, "\n".to_string(), buffer); *tabs += 1; white(false, format!("{}{}\n", " ".repeat(*tabs * 4), info.docs), buffer); *tabs -= 1; } *tabs -= 1; - white(false, format!("\n"), buffer); + white(false, "\n".to_string(), buffer); } } @@ -366,8 +392,8 @@ impl Whamm { }, DataType::Tuple { ty_info: Some(vec![ - Box::new(DataType::Integer), - Box::new(DataType::Integer) + Box::new(DataType::I32), + Box::new(DataType::I32) ]), } ), @@ -901,6 +927,7 @@ pub struct Script { pub providers: HashMap, pub fns: Vec, // User-provided pub globals: HashMap, // User-provided, should be VarId + pub global_stmts: Vec } impl Script { pub fn new() -> Self { @@ -908,7 +935,8 @@ impl Script { name: "".to_string(), providers: HashMap::new(), fns: vec![], - globals: HashMap::new() + globals: HashMap::new(), + global_stmts: vec![], } } @@ -926,7 +954,7 @@ impl Script { None }; return Err(ErrorGen::get_parse_error(true, - Some(format!("Could not find any matches for the provider pattern")), + Some("Could not find any matches for the provider pattern".to_string()), loc, vec![], vec![])); } @@ -953,7 +981,7 @@ impl Script { None }; return Err(ErrorGen::get_parse_error(true, - Some(format!("Could not find any matches for the package pattern")), + Some("Could not find any matches for the package pattern".to_string()), loc, vec![], vec![])); } Ok(package_matches) @@ -983,7 +1011,7 @@ impl Script { None }; return Err(ErrorGen::get_parse_error(true, - Some(format!("Could not find any matches for the event pattern")), + Some("Could not find any matches for the event pattern".to_string()), loc, vec![], vec![])); } Ok(event_matches) @@ -1017,7 +1045,7 @@ impl Script { None }; return Err(ErrorGen::get_parse_error(true, - Some(format!("Could not find any matches for the mode pattern")), + Some("Could not find any matches for the mode pattern".to_string()), loc, vec![], vec![])); } Ok(mode_matches) @@ -1085,7 +1113,7 @@ impl Script { } } white(true, "\n".to_string(), &mut buffer); - white_italics(true, "matches the following providers:\n\n".to_string(), &mut buffer); + grey_italics(true, "matches the following providers:\n\n".to_string(), &mut buffer); } // Print the matched provider information @@ -1094,7 +1122,7 @@ impl Script { continue; } magenta_italics(true, provider_str.clone(), &mut buffer); - white(true, format!(" provider\n"), &mut buffer); + white(true, " provider\n".to_string(), &mut buffer); // Print the provider description tabs += 1; @@ -1127,7 +1155,7 @@ impl Script { } } white(true, "\n".to_string(), &mut buffer); - white_italics(true, "matches the following packages:\n\n".to_string(), &mut buffer); + grey_italics(true, "matches the following packages:\n\n".to_string(), &mut buffer); } // Print the matched package information @@ -1138,7 +1166,7 @@ impl Script { continue; } magenta_italics(true, package_str.clone(), &mut buffer); - white(true, format!(" package\n"), &mut buffer); + white(true, " package\n".to_string(), &mut buffer); // Print the package description tabs += 1; @@ -1162,14 +1190,14 @@ impl Script { white(true, "\n\n".to_string(), &mut buffer); // Print matched event introduction - if !pkg_info.is_empty() { + if !event_info.is_empty() { white(true, format!("{}:{}:", &probe_spec.provider.as_ref().unwrap().name, &probe_spec.package.as_ref().unwrap().name), &mut buffer); magenta(true, format!("{}", &probe_spec.event.as_ref().unwrap().name), &mut buffer); if let Some(mode_patt) = &probe_spec.mode { white(true, format!(":{}", &mode_patt.name), &mut buffer); } white(true, "\n".to_string(), &mut buffer); - white_italics(true, "matches the following events:\n\n".to_string(), &mut buffer); + grey_italics(true, "matches the following events:\n\n".to_string(), &mut buffer); } // Print the matched event information @@ -1181,7 +1209,7 @@ impl Script { continue; } magenta_italics(true, event_str.clone(), &mut buffer); - white(true, format!(" event\n"), &mut buffer); + white(true, " event\n".to_string(), &mut buffer); // Print the event description tabs += 1; @@ -1211,7 +1239,7 @@ impl Script { &probe_spec.package.as_ref().unwrap().name, &probe_spec.event.as_ref().unwrap().name), &mut buffer); magenta(true, format!("{}\n", &probe_spec.mode.as_ref().unwrap().name), &mut buffer); - white_italics(true, "matches the following modes:\n\n".to_string(), &mut buffer); + grey_italics(true, "matches the following modes:\n\n".to_string(), &mut buffer); } // Print the matched mode information @@ -1224,7 +1252,7 @@ impl Script { continue; } magenta_italics(true, mode_str.clone(), &mut buffer); - white(true, format!(" mode\n"), &mut buffer); + white(true, " mode\n".to_string(), &mut buffer); // Print the mode description tabs += 1; @@ -1253,7 +1281,11 @@ impl Script { return Ok(()); } - /// Iterates over all of the matched providers, packages, events, and probe mode names + pub fn add_global_stmts(&mut self, global_statements: Vec) { + self.global_stmts = global_statements; + } + + /// Iterates over all the matched providers, packages, events, and probe mode names /// to add a copy of the user-defined Probe for each of them. pub fn add_probe(&mut self, provided_probes: &ProvidedProbes, probe_spec: &ProbeSpec, predicate: Option, body: Option>) -> Result<(), WhammError> { @@ -1311,9 +1343,9 @@ impl Script { let event = match package.events.get_mut(event_str) { Some(f) => f, None => { - // add the package! - let new_fn = Event::new(event_str.to_lowercase().to_string(), event_patt.loc.clone()); - package.events.insert(event_str.to_lowercase().to_string(), new_fn); + // add the event! + let new_event = Event::new(event_str.to_lowercase().to_string(), event_patt.loc.clone()); + package.events.insert(event_str.to_lowercase().to_string(), new_event); package.events.get_mut(&event_str.to_lowercase()).unwrap() } }; @@ -1348,7 +1380,7 @@ impl Script { if let Some(r) = reason { if let Some(mode_loc) = &r.loc { return Err(ErrorGen::get_parse_error(true, - Some(format!("Could not find any matches for this pattern")), + Some("Could not find any matches for this pattern".to_string()), Some(mode_loc.line_col.clone()), vec![], vec![])); } } @@ -1357,6 +1389,24 @@ impl Script { } } +fn matches_globs(s: &String, globs: &Vec) -> bool { + for glob in globs.iter() { + if glob.matches(s) { + return true; + } + } + false +} + +fn get_globs(patt: &String) -> Vec { + let mut globs = vec![]; + for p in patt.split("|") { + globs.push(Pattern::new(p).unwrap()); + } + + globs +} + #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct ProvidedFunctionality { pub name: String, @@ -1396,11 +1446,11 @@ impl Provider { /// Get the provider names that match the passed glob pattern pub fn get_matches(provided_probes: &ProvidedProbes, prov_patt: &str) -> Vec<(ProvidedFunctionality, String)> { - let glob = Pattern::new(&prov_patt.to_lowercase()).unwrap(); + let globs = get_globs(&prov_patt.to_lowercase()); let mut matches = vec![]; for (provider_name, (info, _provider)) in provided_probes.into_iter() { - if glob.matches(&provider_name.to_lowercase()) { + if matches_globs(&provider_name.to_lowercase(), &globs) { matches.push((info.clone(), provider_name.clone())); } } @@ -1436,18 +1486,51 @@ impl Package { vec![] } - fn get_provided_globals(_name: &String) -> HashMap { - HashMap::new() + fn get_provided_globals(name: &String) -> HashMap { + let mut globals = HashMap::new(); + if name.to_lowercase() == "bytecode" { + // Add in provided globals for the "call" event + globals.insert("tos".to_string(),( + ProvidedFunctionality { + name: "tos".to_string(), + docs: "To get the value on top of the Wasm stack.".to_string() + }, + Global { + is_comp_provided: true, + ty: DataType::I32, + var_name: Expr::VarId { + name: "tos".to_string(), + loc: None + }, + value: None + })); + globals.insert("wasm_bytecode_loc".to_string(),( + ProvidedFunctionality { + name: "wasm_bytecode_loc".to_string(), + docs: "A unique identifier tied to the probe's location in the Wasm bytecode.".to_string() + }, + Global { + is_comp_provided: true, + ty: DataType::I32, + var_name: Expr::VarId { + name: "wasm_bytecode_loc".to_string(), + loc: None + }, + value: None + })); + } + + globals } /// Get the Package names that match the passed glob pattern pub fn get_matches(provided_probes: &ProvidedProbes, provider: &str, mod_patt: &str) -> Vec<(ProvidedFunctionality, String)> { - let glob = Pattern::new(&mod_patt.to_lowercase()).unwrap(); + let globs = get_globs(&mod_patt.to_lowercase()); let mut matches = vec![]; for (mod_name, (info, _package)) in provided_probes.get(provider).unwrap().1.iter() { - if glob.matches(&mod_name.to_lowercase()) { + if matches_globs(&mod_name.to_lowercase(), &globs) { matches.push((info.clone(), mod_name.clone())); } } @@ -1552,12 +1635,12 @@ impl Event { /// Get the Event names that match the passed glob pattern pub fn get_matches(provided_probes: &ProvidedProbes, provider: &str, package: &str, func_patt: &str) -> Vec<(ProvidedFunctionality, String)> { - let glob = Pattern::new(&func_patt.to_lowercase()).unwrap(); + let globs = get_globs(&func_patt.to_lowercase()); let mut matches = vec![]; for (fn_name, (info, _package)) in provided_probes.get(provider).unwrap().1.get(package).unwrap().1.iter() { - if glob.matches(&fn_name.to_lowercase()) { + if matches_globs(&fn_name.to_lowercase(), &globs) { matches.push((info.clone(), fn_name.clone())); } } @@ -1614,12 +1697,12 @@ impl Probe { /// Get the Probe modes that match the passed glob pattern pub fn get_matches(provided_probes: &ProvidedProbes, provider: &str, package: &str, event: &str, mode_patt: &str) -> Vec<(ProvidedFunctionality, String)> { - let glob = Pattern::new(&mode_patt.to_lowercase()).unwrap(); + let globs = get_globs(&mode_patt.to_lowercase()); let mut matches = vec![]; for (info, m_name) in provided_probes.get(provider).unwrap().1.get(package).unwrap().1.get(event).unwrap().1.iter() { - if glob.matches(&m_name.to_lowercase()) { + if matches_globs(&m_name.to_lowercase(), &globs) { matches.push((info.clone(), m_name.clone())); } } diff --git a/src/parser/whamm.pest b/src/parser/whamm.pest index b79e3943..bece6dae 100644 --- a/src/parser/whamm.pest +++ b/src/parser/whamm.pest @@ -2,21 +2,18 @@ // ---- High-Level Structure ---- // ============================== -script = { SOI ~ probe_def+ ~ EOI } +// supports top-level global declarations/initial assignments and probe definitions +script = { SOI ~ statement* ~ probe_def+ ~ EOI } // TODO -- support comma separated list of specs: https://docs.oracle.com/cd/E23824_01/html/E22973/glghi.html#scrolltoc probe_def = { PROBE_SPEC ~ PUSH(predicate?) ~ "{" ~ statement* ~ "}" } predicate = { "/" ~ expr ~ "/" } -// spec = { PROBE_SPEC | PROBE_ID } - // ===================== // ---- Identifiers ---- // ===================== -ID = @{ (ASCII_ALPHA | "_")+ ~ ( ASCII_DIGIT | (ASCII_ALPHA | "_")+ )* } - PROBE_ID = @{ (ASCII_ALPHA | "_" @@ -25,6 +22,7 @@ PROBE_ID = @{ | "+" | "\\" | "?" + | "|" | "!" | "[" | "]")+ @@ -38,6 +36,23 @@ PROBE_SPEC = ${ | PROBE_ID } +ID = @{ (ASCII_ALPHA | "_")+ ~ ( ASCII_DIGIT | (ASCII_ALPHA | "_")+ )* } + +// =============== +// ---- Types ---- +// =============== + +TY_I32 = @{ "i32" } +TY_BOOL = @{ "bool" } +TY_STRING = @{ "str" } + +// a tuple that's used as a type declaration +TY_TUPLE = { "(" ~ TYPE ~ ( "," ~ TYPE )* ~ ")" } +// first TYPE is the type of the key, second TYPE is the type of the value +TY_MAP = { "map<" ~ TYPE ~ "," ~ TYPE ~ ">" } + +TYPE = _{ TY_I32 | TY_BOOL | TY_STRING | TY_TUPLE | TY_MAP } + // ==================== // ---- Statements ---- // ==================== @@ -45,17 +60,21 @@ PROBE_SPEC = ${ arg = { tuple | expr | val } fn_call = { ID ~ "(" ~ ( arg )? ~ ( "," ~ arg )* ~ ")" } -assignment = { ID ~ "=" ~ expr } -statement = { ( ( fn_call | assignment ) ~ ";" )+ } +// var ops +declaration = { TYPE ~ ID } +assignment = { ID ~ "=" ~ (ternary | expr) } +statement = { ( ( declaration | assignment | fn_call ) ~ ";" )+ } // ===================== // ---- Expressions ---- // ===================== +ternary = { expr ~ "?" ~ expr ~ ":" ~ expr } + expr = { operand ~ (BINOP ~ operand)* } -val = _{ BOOL | ID | INT | STRING } -operand = { fn_call | "(" ~ expr ~ ")" | val } +val = _{ BOOL | ID | I32 | STRING } +operand = _{ fn_call | "(" ~ expr ~ ")" | val } tuple = { "(" ~ (val) ~ ( "," ~ val )* ~ ")" } @@ -92,7 +111,7 @@ BINOP = _{ LOGOP | RELOP | SUMOP | MULOP } // TODO -- make sure that the binary/octal/hexadecimal formats are parsed correctly // TODO -- add support for negative numbers (see calc-pest tutorial) -INT = @{ +I32 = @{ "0x" ~ ASCII_HEX_DIGIT+ // Hexadecimal digit | "0b" ~ ASCII_BIN_DIGIT+ // Binary digit | "0" ~ ASCII_OCT_DIGIT+ // Octal digit diff --git a/src/parser/whamm_parser.rs b/src/parser/whamm_parser.rs index 4b2af733..08d154ac 100644 --- a/src/parser/whamm_parser.rs +++ b/src/parser/whamm_parser.rs @@ -4,7 +4,7 @@ use types::{WhammParser, Op, PRATT_PARSER, Rule}; use pest::error::{Error, LineColLocation}; use pest::Parser; -use pest::iterators::{Pair, Pairs}; +use pest::iterators::Pair; use log::{trace}; use crate::common::error::{ErrorGen, WhammError}; @@ -117,6 +117,23 @@ pub fn process_pair(whamm: &mut Whamm, script_count: usize, pair: Pair, er process_pair(whamm, id, p, err); }); trace!("Exiting script"); + }, + Rule::statement => { + trace!("Entering statement"); + + // let mut pair = pair.into_inner(); + // let stmt_rules = pair.next().unwrap(); + + let mut global_stmts = vec![]; + pair.into_inner().for_each(|p| { + global_stmts.push(stmt_from_rule(p, err)); + }); + + // Add global statements to the script + let script: &mut Script = whamm.scripts.get_mut(script_count).unwrap(); + script.add_global_stmts(global_stmts); + + trace!("Exiting statement"); } Rule::probe_def => { trace!("Entering probe_def"); @@ -131,7 +148,7 @@ pub fn process_pair(whamm: &mut Whamm, script_count: usize, pair: Pair, er Some(n) => { let (this_predicate, mut this_body) = match n.as_rule() { Rule::predicate => { - match expr_from_pairs(n.into_inner()) { + match expr_from_pair(n.into_inner().next().unwrap()) { Ok(res) => (Some(res), None), Err(errors) => { err.add_errors(errors); @@ -215,7 +232,7 @@ fn fn_call_from_rule(pair: Pair) -> Result> { let mut errors = vec![]; while next.is_some() { let mut others = vec!(); - match expr_from_pairs(next.unwrap().into_inner()) { + match expr_from_pair(next.unwrap()) { Ok(expr) => { others.push(Box::new(expr)); init.append(&mut others); @@ -266,7 +283,6 @@ fn fn_call_from_rule(pair: Pair) -> Result> { }) }) } - } fn stmt_from_rule(pair: Pair, err: &mut ErrorGen) -> Statement { @@ -275,16 +291,39 @@ fn stmt_from_rule(pair: Pair, err: &mut ErrorGen) -> Statement { Rule::statement => { trace!("Entering statement"); let res = stmt_from_rule(pair, err); - trace!("Exiting statement"); - trace!("Exiting stmt_from_rule"); return res; }, + Rule::declaration => { + trace!("Entering declaration"); + // declaration = { TYPE ~ ID } + let mut pair = pair.into_inner(); + let type_rule = pair.next().unwrap(); + let type_line_col = LineColLocation::from(type_rule.as_span()); + let ty = type_from_rule(type_rule, err); + + let var_id_rule = pair.next().unwrap(); + let var_id_line_col = LineColLocation::from(var_id_rule.as_span()); + let var_id = Expr::VarId { + name: var_id_rule.as_str().parse().unwrap(), + loc: Some(Location { + line_col: var_id_line_col.clone(), + path: None + }) + }; + trace!("Exiting declaration"); + + Statement::Decl { + ty, + var_id, + loc: Some(Location::from(&type_line_col, &var_id_line_col, None)) + } + }, Rule::assignment => { trace!("Entering assignment"); let mut pair = pair.into_inner(); let var_id_rule = pair.next().unwrap(); - let expr_rule = pair.next().unwrap().into_inner(); + let expr_rule = pair.next().unwrap(); let var_id_line_col = LineColLocation::from(var_id_rule.as_span()); @@ -296,7 +335,7 @@ fn stmt_from_rule(pair: Pair, err: &mut ErrorGen) -> Statement { }) }; - return match expr_from_pairs(expr_rule) { + return match expr_from_pair(expr_rule) { Err(errors) => { err.add_errors(errors); Statement::dummy() @@ -347,7 +386,59 @@ fn stmt_from_rule(pair: Pair, err: &mut ErrorGen) -> Statement { } } -fn probe_spec_part_from_rule(pair: Pair, err: &mut ErrorGen) -> SpecPart { +fn type_from_rule(pair: Pair, err: &mut ErrorGen) -> DataType { + trace!("Entering type_from_rule"); + // TYPE = _{ TY_I32 | TY_BOOL | TY_STRING | TY_TUPLE | TY_MAP } + return match pair.as_rule() { + Rule::TY_I32 => { + DataType::I32 + }, + Rule::TY_BOOL => { + DataType::Boolean + }, + Rule::TY_STRING => { + DataType::Str + }, + Rule::TY_TUPLE => { + let mut tuple_content_types = vec![]; + pair.into_inner().for_each(|p| { + tuple_content_types.push(Box::new(type_from_rule(p, err))); + }); + return if tuple_content_types.is_empty() { + DataType::Tuple { + ty_info: None + } + } else { + DataType::Tuple { + ty_info: Some(tuple_content_types) + } + } + }, + Rule::TY_MAP => { + let mut pair = pair.into_inner(); + let key_ty_rule = pair.next().unwrap(); + let val_ty_rule = pair.next().unwrap(); + + let key_ty = type_from_rule(key_ty_rule, err); + let val_ty = type_from_rule(val_ty_rule, err); + + return DataType::Map { + key_ty: Box::new(key_ty), + val_ty: Box::new(val_ty) + } + }, + rule => { + err.parse_error(true, + Some(UNEXPECTED_ERR_MSG.to_string()), + Some(LineColLocation::from(pair.as_span())), + vec![Rule::TY_I32, Rule::TY_BOOL, Rule::TY_STRING, Rule::TY_TUPLE, Rule::TY_MAP], vec![rule]); + // should have exited above (since it's a fatal error) + unreachable!(); + } + }; +} + +fn probe_spec_part_from_rule(pair: Pair, err: &mut ErrorGen) -> SpecPart { trace!("Entered probe_spec_part_from_rule"); match pair.as_rule() { Rule::PROBE_ID => { @@ -512,14 +603,14 @@ fn expr_primary(pair: Pair) -> Result> { }) }); }, - Rule::INT => { - trace!("Entering INT"); + Rule::I32 => { + trace!("Entering I32"); let val = pair.as_str().parse::().unwrap(); - trace!("Exiting INT"); + trace!("Exiting I32"); return Ok(Expr::Primitive { val: Value::Integer { - ty: DataType::Integer, + ty: DataType::I32, val }, loc: Some(Location { @@ -567,81 +658,142 @@ fn expr_primary(pair: Pair) -> Result> { }) }); }, - _ => expr_from_pairs(pair.into_inner()) + _ => expr_from_pair(pair) } } -fn expr_from_pairs(pairs: Pairs) -> Result> { - PRATT_PARSER - .map_primary(|primary| -> Result> { - expr_primary(primary) - }) - .map_infix(|lhs, op, rhs| -> Result> { - return match (lhs, rhs) { - (Ok(lhs), Ok(rhs)) => { - let op = match op.as_rule() { - // Logical operators - Rule::and => Op::And, - Rule::or => Op::Or, - - // Relational operators - Rule::eq => Op::EQ, - Rule::ne => Op::NE, - Rule::ge => Op::GE, - Rule::gt => Op::GT, - Rule::le => Op::LE, - Rule::lt => Op::LT, - - // Highest precedence arithmetic operators - Rule::add => Op::Add, - Rule::subtract => Op::Subtract, - - // Next highest precedence arithmetic operators - Rule::multiply => Op::Multiply, - Rule::divide => Op::Divide, - Rule::modulo => Op::Modulo, - rule => { - return Err(vec![ErrorGen::get_parse_error( - true, - Some(UNEXPECTED_ERR_MSG.to_string()), - Some(LineColLocation::from(op.as_span())), - vec![Rule::and, Rule::or, Rule::eq, Rule::ne, Rule::ge, Rule::gt, Rule::le, Rule::lt, - Rule::add, Rule::subtract, Rule::multiply, Rule::divide, Rule::modulo], - vec![rule])]); - }, - }; - - let lhs_line_col = if let Some(lhs_loc) = lhs.loc() { - LineColLocation::from(lhs_loc.line_col.clone()) - } else { - exit(1); - }; - - let rhs_line_col = if let Some(rhs_loc) = rhs.loc() { - LineColLocation::from(rhs_loc.line_col.clone()) - } else { - exit(1); - }; +fn expr_from_pair(pair: Pair) -> Result> { + return match pair.as_rule() { + Rule::ternary => { + // handle contents + let pair_loc = LineColLocation::from(pair.as_span()); + let mut pairs = pair.into_inner(); + + let cond_rule = pairs.next().unwrap(); + let cond = match expr_from_pair(cond_rule) { + Ok(expr) => expr, + other => { + return other; + } + }; - Ok(Expr::BinOp { - lhs: Box::new(lhs), - op, - rhs: Box::new(rhs), - loc: Some(Location::from(&lhs_line_col, &rhs_line_col, None)) - }) - }, - (lhs, rhs) => { - let mut errors = vec![]; - if let Err(lhs_err) = lhs { - errors.extend(lhs_err); - } - if let Err(rhs_err) = rhs { - errors.extend(rhs_err); - } + let conseq_rule = pairs.next().unwrap(); + let conseq = match expr_from_pair(conseq_rule) { + Ok(expr) => expr, + other => { + return other; + } + }; - Err(errors) + let alt_rule = pairs.next().unwrap(); + let alt = match expr_from_pair(alt_rule) { + Ok(expr) => expr, + other => { + return other; } }; - }) - .parse(pairs) + + Ok(Expr::Ternary { + cond: Box::new(cond), + conseq: Box::new(conseq), + alt: Box::new(alt), + loc: Some(Location { + line_col: pair_loc, + path: None + }) + }) + }, + Rule::arg => { + let mut pairs = pair.into_inner(); + let arg = pairs.next().unwrap(); + match arg.as_rule() { + Rule::expr => expr_from_pair(arg), + _ => expr_primary(arg) + } + }, + Rule::expr => { + let pairs = pair.into_inner(); + // TODO -- try boxing ErrorGen so you can put it in both closures? + PRATT_PARSER + .map_primary(|primary| -> Result> { + expr_primary(primary) + }) + .map_infix(|lhs, op, rhs| -> Result> { + return match (lhs, rhs) { + (Ok(lhs), Ok(rhs)) => { + let op = match op.as_rule() { + // Logical operators + Rule::and => Op::And, + Rule::or => Op::Or, + + // Relational operators + Rule::eq => Op::EQ, + Rule::ne => Op::NE, + Rule::ge => Op::GE, + Rule::gt => Op::GT, + Rule::le => Op::LE, + Rule::lt => Op::LT, + + // Highest precedence arithmetic operators + Rule::add => Op::Add, + Rule::subtract => Op::Subtract, + + // Next highest precedence arithmetic operators + Rule::multiply => Op::Multiply, + Rule::divide => Op::Divide, + Rule::modulo => Op::Modulo, + rule => { + return Err(vec![ErrorGen::get_parse_error( + true, + Some(UNEXPECTED_ERR_MSG.to_string()), + Some(LineColLocation::from(op.as_span())), + vec![Rule::and, Rule::or, Rule::eq, Rule::ne, Rule::ge, Rule::gt, Rule::le, Rule::lt, + Rule::add, Rule::subtract, Rule::multiply, Rule::divide, Rule::modulo], + vec![rule])]); + }, + }; + + let lhs_line_col = if let Some(lhs_loc) = lhs.loc() { + LineColLocation::from(lhs_loc.line_col.clone()) + } else { + exit(1); + }; + + let rhs_line_col = if let Some(rhs_loc) = rhs.loc() { + LineColLocation::from(rhs_loc.line_col.clone()) + } else { + exit(1); + }; + + Ok(Expr::BinOp { + lhs: Box::new(lhs), + op, + rhs: Box::new(rhs), + loc: Some(Location::from(&lhs_line_col, &rhs_line_col, None)) + }) + }, + (lhs, rhs) => { + let mut errors = vec![]; + if let Err(lhs_err) = lhs { + errors.extend(lhs_err); + } + if let Err(rhs_err) = rhs { + errors.extend(rhs_err); + } + + Err(errors) + } + }; + }) + .parse(pairs) + }, + rule => { + Err(vec![ErrorGen::get_parse_error( + true, + Some(UNEXPECTED_ERR_MSG.to_string()), + Some(LineColLocation::from(pair.as_span())), + vec![Rule::expr, Rule::ternary], + vec![rule])]) + } + } } \ No newline at end of file diff --git a/src/verifier/builder_visitor.rs b/src/verifier/builder_visitor.rs index 40670b76..c90958cf 100644 --- a/src/verifier/builder_visitor.rs +++ b/src/verifier/builder_visitor.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; use crate::parser::types as parser_types; -use parser_types::{DataType, Script, Whamm, WhammVisitor, Expr, Fn, Event, Package, Op, Probe, Provider, Statement, Value}; +use parser_types::{DataType, Script, Whamm, Expr, Fn, Event, Package, Op, Probe, Provider, Statement, Value}; use crate::verifier::types::{Record, ScopeType, SymbolTable}; use log::trace; use crate::common::error::ErrorGen; -use crate::parser::types::{Global, ProvidedFunctionality}; +use crate::parser::types::{Global, ProvidedFunctionality, WhammVisitorMut}; const UNEXPECTED_ERR_MSG: &str = "SymbolTableBuilder: Looks like you've found a bug...please report this behavior! Exiting now..."; @@ -213,12 +213,12 @@ impl SymbolTableBuilder<'_> { self.table.set_curr_scope_info(probe.mode.clone(), ScopeType::Probe); } - fn add_fn(&mut self, f: &Fn) { + fn add_fn(&mut self, f: &mut Fn) { let f_name = &f.name; if let Some(other_fn_id) = self.table.lookup(&f_name.name) { if let Some(other_rec) = self.table.get_record(other_fn_id) { if let (Some(curr_loc), Some(other_loc))= (&f_name.loc, other_rec.loc()) { - self.err.duplicate_identifier_error(false, f_name.name.clone(), curr_loc.line_col.clone(), other_loc.line_col.clone()); + self.err.duplicate_identifier_error(false, f_name.name.clone(), Some(curr_loc.line_col.clone()), Some(other_loc.line_col.clone())); } else { // This should never be the case since it's controlled by the compiler! self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); @@ -255,7 +255,23 @@ impl SymbolTableBuilder<'_> { self.table.set_curr_scope_info(f.name.name.clone(), ScopeType::Fn); // visit parameters - f.params.iter().for_each(| param | self.visit_formal_param(param)); + f.params.iter_mut().for_each(| param | self.visit_formal_param(param)); + } + + fn add_global_id_to_curr_rec(&mut self, id: usize) { + match self.table.get_curr_rec_mut() { + Some(Record::Whamm { globals, .. }) | + Some(Record::Script { globals, .. }) | + Some(Record::Provider { globals, .. }) | + Some(Record::Package { globals, .. }) | + Some(Record::Event { globals, .. }) | + Some(Record::Probe { globals, .. }) => { + globals.push(id.clone()); + } + _ => { + self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); + } + } } fn add_fn_id_to_curr_rec(&mut self, id: usize) { @@ -325,7 +341,7 @@ impl SymbolTableBuilder<'_> { }); // add global record to the current record - self.add_fn_id_to_curr_rec(id); + self.add_global_id_to_curr_rec(id); } fn visit_provided_globals(&mut self, globals: &HashMap) { @@ -333,16 +349,10 @@ impl SymbolTableBuilder<'_> { self.add_global(global.ty.clone(), name.clone()); } } - - fn visit_globals(&mut self, globals: &HashMap) { - for (name, global) in globals.iter() { - self.add_global(global.ty.clone(), name.clone()); - } - } } -impl WhammVisitor<()> for SymbolTableBuilder<'_> { - fn visit_whamm(&mut self, whamm: &Whamm) -> () { +impl WhammVisitorMut<()> for SymbolTableBuilder<'_> { + fn visit_whamm(&mut self, whamm: &mut Whamm) -> () { trace!("Entering: visit_whamm"); let name: String = "whamm".to_string(); self.table.set_curr_scope_info(name.clone(), ScopeType::Whamm); @@ -361,25 +371,47 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_whamm = Some(id); // visit fns - whamm.fns.iter().for_each(| (.., f) | self.visit_fn(f) ); + whamm.fns.iter_mut().for_each(| (.., f) | self.visit_fn(f) ); // visit globals self.visit_provided_globals(&whamm.globals); // visit scripts - whamm.scripts.iter().for_each(| script | self.visit_script(script)); + whamm.scripts.iter_mut().for_each(| script | self.visit_script(script)); trace!("Exiting: visit_whamm"); self.curr_whamm = None; } - fn visit_script(&mut self, script: &Script) -> () { + fn visit_script(&mut self, script: &mut Script) -> () { trace!("Entering: visit_script"); self.add_script(script); - script.fns.iter().for_each(| f | self.visit_fn(f) ); - self.visit_globals(&script.globals); - script.providers.iter().for_each(| (_name, provider) | { + + script.fns.iter_mut().for_each(| f | self.visit_fn(f) ); + script.global_stmts.iter_mut().for_each(|stmt| { + match stmt { + Statement::Decl {ty, var_id, ..} => { + if let Expr::VarId {name, ..} = &var_id { + // Add global variable to script globals (triggers the init_generator to emit them!) + script.globals.insert(name.clone(), Global { + is_comp_provided: false, + ty: ty.clone(), + var_name: var_id.clone(), + value: None, + }); + } else { + self.err.unexpected_error(true, Some(format!("{} \ + Variable declaration var_id is not the correct Expr variant!!", UNEXPECTED_ERR_MSG.to_string())), None); + } + }, + // We only care about building the symbols right now, not actual operations + _ => {} + } + + self.visit_stmt(stmt) + }); + script.providers.iter_mut().for_each(| (_name, provider) | { self.visit_provider(provider) }); @@ -391,13 +423,13 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_script = None; } - fn visit_provider(&mut self, provider: &Provider) -> () { + fn visit_provider(&mut self, provider: &mut Provider) -> () { trace!("Entering: visit_provider"); self.add_provider(provider); - provider.fns.iter().for_each(| (.., f) | self.visit_fn(f) ); + provider.fns.iter_mut().for_each(| (.., f) | self.visit_fn(f) ); self.visit_provided_globals(&provider.globals); - provider.packages.iter().for_each(| (_name, package) | { + provider.packages.iter_mut().for_each(| (_name, package) | { self.visit_package(package) }); @@ -409,13 +441,13 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_provider = None; } - fn visit_package(&mut self, package: &Package) -> () { + fn visit_package(&mut self, package: &mut Package) -> () { trace!("Entering: visit_package"); self.add_package(package); - package.fns.iter().for_each(| (.., f) | self.visit_fn(f) ); + package.fns.iter_mut().for_each(| (.., f) | self.visit_fn(f) ); self.visit_provided_globals(&package.globals); - package.events.iter().for_each(| (_name, event) | { + package.events.iter_mut().for_each(| (_name, event) | { self.visit_event(event) }); @@ -427,16 +459,16 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_package = None; } - fn visit_event(&mut self, event: &Event) -> () { + fn visit_event(&mut self, event: &mut Event) -> () { trace!("Entering: visit_event"); self.add_event(event); - event.fns.iter().for_each(| (.., f) | self.visit_fn(f) ); + event.fns.iter_mut().for_each(| (.., f) | self.visit_fn(f) ); self.visit_provided_globals(&event.globals); // visit probe_map - event.probe_map.iter().for_each(| probes | { - probes.1.iter().for_each(| probe | { + event.probe_map.iter_mut().for_each(| probes | { + probes.1.iter_mut().for_each(| probe | { self.visit_probe(probe); }); }); @@ -449,11 +481,11 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_event = None; } - fn visit_probe(&mut self, probe: &Probe) -> () { + fn visit_probe(&mut self, probe: &mut Probe) -> () { trace!("Entering: visit_probe"); self.add_probe(probe); - probe.fns.iter().for_each(| (.., f) | self.visit_fn(f) ); + probe.fns.iter_mut().for_each(| (.., f) | self.visit_fn(f) ); self.visit_provided_globals(&probe.globals); // Will not visit predicate/body at this stage @@ -466,7 +498,7 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_probe = None; } - fn visit_fn(&mut self, f: &Fn) -> () { + fn visit_fn(&mut self, f: &mut Fn) -> () { trace!("Entering: visit_fn"); // add fn @@ -482,7 +514,7 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { self.curr_fn = None; } - fn visit_formal_param(&mut self, param: &(Expr, DataType)) -> () { + fn visit_formal_param(&mut self, param: &mut (Expr, DataType)) -> () { trace!("Entering: visit_formal_param"); // add param @@ -491,27 +523,43 @@ impl WhammVisitor<()> for SymbolTableBuilder<'_> { trace!("Exiting: visit_formal_param"); } - fn visit_stmt(&mut self, _assign: &Statement) -> () { - // Not visiting event/probe bodies - self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); + fn visit_stmt(&mut self, stmt: &mut Statement) -> () { + if self.curr_provider.is_some() || self.curr_package.is_some() || self.curr_event.is_some() || self.curr_probe.is_some() { + self.err.unexpected_error(true, Some(format!("{} \ + Only global script statements should be visited!", UNEXPECTED_ERR_MSG.to_string())), None); + } + + match stmt { + Statement::Decl {ty, var_id, ..} => { + if let Expr::VarId {name, ..} = &var_id { + // Add symbol to table + self.add_global(ty.clone(), name.clone()); + } else { + self.err.unexpected_error(true, Some(format!("{} \ + Variable declaration var_id is not the correct Expr variant!!", UNEXPECTED_ERR_MSG.to_string())), None); + } + }, + // We only care about building the symbols right now, not actual operations + _ => {} + } } - fn visit_expr(&mut self, _call: &Expr) -> () { + fn visit_expr(&mut self, _call: &mut Expr) -> () { // Not visiting predicates/statements self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); } - fn visit_op(&mut self, _op: &Op) -> () { + fn visit_op(&mut self, _op: &mut Op) -> () { // Not visiting predicates/statements self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); } - fn visit_datatype(&mut self, _datatype: &DataType) -> () { + fn visit_datatype(&mut self, _datatype: &mut DataType) -> () { // Not visiting predicates/statements self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); } - fn visit_value(&mut self, _val: &Value) -> () { + fn visit_value(&mut self, _val: &mut Value) -> () { // Not visiting predicates/statements self.err.unexpected_error(true, Some(UNEXPECTED_ERR_MSG.to_string()), None); } diff --git a/src/verifier/tests.rs b/src/verifier/tests.rs index 3604d73a..8fac3d99 100644 --- a/src/verifier/tests.rs +++ b/src/verifier/tests.rs @@ -31,8 +31,8 @@ pub fn test_build_table() { for script in VALID_SCRIPTS { match tests::get_ast(script, &mut err) { - Some(ast) => { - let table = verifier::build_symbol_table(&ast, &mut err); + Some(mut ast) => { + let table = verifier::build_symbol_table(&mut ast, &mut err); println!("{:#?}", table); }, None => { @@ -60,15 +60,15 @@ wasm::call:alt / let mut err = ErrorGen::new("".to_string(), "".to_string(), 0); match tests::get_ast(script, &mut err) { - Some(ast) => { - let table = verifier::build_symbol_table(&ast, &mut err); + Some(mut ast) => { + let table = verifier::build_symbol_table(&mut ast, &mut err); println!("{:#?}", table); // 7 scopes: whamm, strcmp, script, wasm, bytecode, call, alt let num_scopes = 7; // records: num_scopes PLUS (target_fn_type, target_imp_module, target_imp_name, new_target_fn_name, - // str_addr, value) - let num_recs = num_scopes + 6; + // tos, wasm_bytecode_loc, str_addr, value) + let num_recs = num_scopes + 8; // asserts on very high level table structure assert_eq!(num_scopes, table.scopes.len()); diff --git a/src/verifier/verifier.rs b/src/verifier/verifier.rs index 5c694434..e3af3dc5 100644 --- a/src/verifier/verifier.rs +++ b/src/verifier/verifier.rs @@ -1,9 +1,9 @@ use crate::common::error::ErrorGen; -use crate::parser::types::{Whamm, WhammVisitor}; +use crate::parser::types::{Whamm, WhammVisitorMut}; use crate::verifier::builder_visitor::SymbolTableBuilder; use crate::verifier::types::SymbolTable; -pub fn build_symbol_table(ast: &Whamm, err: &mut ErrorGen) -> SymbolTable { +pub fn build_symbol_table(ast: &mut Whamm, err: &mut ErrorGen) -> SymbolTable { let mut visitor = SymbolTableBuilder { table: SymbolTable::new(), err, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index bc6cb52d..85984cf3 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -82,15 +82,15 @@ fn process_scripts(scripts: Vec<(PathBuf, String)>, err: &mut ErrorGen) -> Vec<( // Build the symbol table from the AST let mut result = vec![]; - for (path, script_str, ast) in asts { - let mut symbol_table = build_symbol_table(&ast, err); + for (path, script_str, mut ast) in asts { + let mut symbol_table = build_symbol_table(&mut ast, err); symbol_table.reset(); // Build the behavior tree from the AST let (mut behavior, simple_ast) = build_behavior_tree(&ast, err); behavior.reset(); - result.push((path.into_os_string().into_string().unwrap(), script_str, ast, symbol_table, behavior, simple_ast)); + result.push((path.into_os_string().into_string().unwrap(), script_str.clone(), ast, symbol_table, behavior, simple_ast)); } result diff --git a/tests/integration_test.rs b/tests/integration_test.rs index d394af1a..f036d455 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -117,4 +117,4 @@ fn instrument_with_replay() { let processed_scripts = common::setup_replay(&mut err); // TODO -- change this when you've supported this monitor type assert_eq!(processed_scripts.len(), 0); -} \ No newline at end of file +} diff --git a/tests/scripts/wizard_monitors/branch-simple.mm b/tests/scripts/wizard_monitors/branch-simple.mm new file mode 100644 index 00000000..444cd375 --- /dev/null +++ b/tests/scripts/wizard_monitors/branch-simple.mm @@ -0,0 +1,11 @@ +// TODO -- the key needs to be changed to "(wasm_bytecode_loc, i32)" +//map<(i32, i32), i32> count; // TODO -- switch to map! +i32 count; +wasm::br|br_if:alt { + i32 index; + // "tos" is defined as the top-of-stack + index = tos != 0 ? 1 : 0; + // count stores an array of counters + // TODO -- add map op support and uncomment the following + // count[probe_func, pc, index]++; +} \ No newline at end of file diff --git a/tests/scripts/wizard_monitors/branch.mm.TODO b/tests/scripts/wizard_monitors/branch.mm.TODO index 9067fc41..689f4794 100644 --- a/tests/scripts/wizard_monitors/branch.mm.TODO +++ b/tests/scripts/wizard_monitors/branch.mm.TODO @@ -11,7 +11,7 @@ */ // matches "if" and "br_if" bytecodes -wasm:::[br_]if { +wasm:::br|br_if { // "tos" is defined as the top-of-stack int index = tos != 0 ? 1 : 0; // count stores an array of counters diff --git a/wasm_playground/control_flow/Cargo.toml b/wasm_playground/control_flow/Cargo.toml new file mode 100644 index 00000000..e5d839e5 --- /dev/null +++ b/wasm_playground/control_flow/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "add" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +doc = false +name = "cf" +path = "src/main.rs" + +[dependencies] +wasm-bindgen = "0.2" \ No newline at end of file diff --git a/wasm_playground/control_flow/src/main.rs b/wasm_playground/control_flow/src/main.rs new file mode 100644 index 00000000..cbc9af04 --- /dev/null +++ b/wasm_playground/control_flow/src/main.rs @@ -0,0 +1,16 @@ +#[no_mangle] +pub fn add(a: i32, b: i32) -> i32 { + let res = a + b; + + return res; +} + +fn main() { + let a = 0; + let mut b = 3; + let c = add(a, b); + + if c > 0 { + b = 5; + } +} \ No newline at end of file diff --git a/wasm_playground/strcmp/strcmp.wasm b/wasm_playground/strcmp/strcmp.wasm new file mode 100644 index 00000000..c864c515 Binary files /dev/null and b/wasm_playground/strcmp/strcmp.wasm differ diff --git a/wasm_playground/strcmp.wat b/wasm_playground/strcmp/strcmp.wat similarity index 100% rename from wasm_playground/strcmp.wat rename to wasm_playground/strcmp/strcmp.wat