diff --git a/Cargo.toml b/Cargo.toml index efe3d5d..23e6373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ members = [ "rula-lib", "rula-parser", "rula-exec", + "ruleset-ir", "tests-e2e" ] \ No newline at end of file diff --git a/doc/design.md b/doc/design.md deleted file mode 100644 index 37bde7f..0000000 --- a/doc/design.md +++ /dev/null @@ -1 +0,0 @@ -# Design decisions go here \ No newline at end of file diff --git a/doc/spec.md b/doc/spec.md new file mode 100644 index 0000000..fc34661 --- /dev/null +++ b/doc/spec.md @@ -0,0 +1,8 @@ +# RuLa Specification +... + +## Lifetime and Scope +Scope names: + +\_\_@global\_\_ ... A global scope + diff --git a/rula-exec/Cargo.toml b/rula-exec/Cargo.toml index 02cad0e..98dcf4b 100644 --- a/rula-exec/Cargo.toml +++ b/rula-exec/Cargo.toml @@ -7,7 +7,8 @@ description = "Generate executable Rust code for RuLa program" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rula_parser = { path = "../rula-parser" } +rula-parser = { path = "../rula-parser" } +ruleset-ir = {path = "../ruleset-ir"} # (rula_ast -> rula_program(rust syntax tree) -> quote -> rust_source) quote = "1.0" syn = {version = "1.0", features = ["full"]} diff --git a/rula-exec/src/generator/error.rs b/rula-exec/src/generator/error.rs new file mode 100644 index 0000000..d890de2 --- /dev/null +++ b/rula-exec/src/generator/error.rs @@ -0,0 +1,5 @@ +#[derive(Debug)] +pub enum CompileError{ + ParentScopeNotFoundError, + ParentScopeDroppedError, +} \ No newline at end of file diff --git a/rula-exec/src/generator/generator.rs b/rula-exec/src/generator/generator.rs new file mode 100644 index 0000000..3b3e376 --- /dev/null +++ b/rula-exec/src/generator/generator.rs @@ -0,0 +1,103 @@ +use super::scope::Scope; +use super::{symbol_table::SymbolTable, IResult}; +use rula_parser::parser::ast::*; +use ruleset_ir::instructions::*; +use ruleset_ir::RuleSetIR; +use std::cell::RefCell; +use std::rc::Rc; + +// Create symbol table to track new symbols +// type RefSymbolTable = RefCell; +type RcRefCellScope = Rc>; + +// Convert RuLa expressions into RuleSet IR +pub fn generate_ir(ast_tree: &AstNode) -> IResult { + // create symbol table to track new symbols + // let symbol_table = RefCell::new(SymbolTable::new()); + // Prepare a root global scope + let global_scope = Rc::new(RefCell::new(Scope::new("__@global"))); + global_scope.borrow_mut().set_root(); + + // Generate ir segment + let mut ir_segment = RuleSetIR::new(); + let mut generated_ir = match ast_tree { + AstNode::RuLa(rula) => generate_rula_ir(rula, &global_scope).unwrap(), + AstNode::PlaceHolder => todo!(), + }; + + ir_segment.merge(&mut generated_ir); + Ok(ir_segment) +} + +fn generate_rula_ir(rula: &RuLa, parent_scope: &RcRefCellScope) -> IResult { + // At this level, new scope is not introduced. + // Ir segment for rula_ir + let mut rula_ir_segment = RuleSetIR::new(); + let mut generated_ir = match &*rula.rula { + RuLaKind::Program(program) => generate_program_ir(program, &parent_scope).unwrap(), + RuLaKind::Ignore => { + let mut ignorant_ir = RuleSetIR::new(); + ignorant_ir.add_instruction(Box::new(NOP::new(None))); + ignorant_ir + } + _ => todo!(), + }; + rula_ir_segment.merge(&mut generated_ir); + Ok(rula_ir_segment) +} + +fn generate_program_ir(program: &Program, parent_scope: &RcRefCellScope) -> IResult { + // For program level ir, prepare segment ir for all the program asts + let mut program_ir_segment = RuleSetIR::new(); + for prog in program.programs.iter() { + let mut generated_ir = match prog { + ProgramKind::Import(import) => generate_import_ir(import, parent_scope).unwrap(), + ProgramKind::RuleSetExpr(ruleset) => { + generate_ruleset_expr_ir(ruleset, parent_scope).unwrap() + } + ProgramKind::RuleExpr(rule) => generate_rule_expr_ir(rule, parent_scope).unwrap(), + ProgramKind::Repeaters => { + todo!() + } + }; + + program_ir_segment.merge(&mut generated_ir) + } + Ok(program_ir_segment) +} + +fn generate_import_ir(import: &Import, parent_scope: &RcRefCellScope) -> IResult { + + + Ok(RuleSetIR::new()) +} + +fn generate_ruleset_expr_ir( + ruleset_expr: &RuleSetExpr, + parent_scope: &RcRefCellScope, +) -> IResult { + let ruleset_name = &*ruleset_expr.name.name; + + // Introduce a new scope for ruleset clause + let ruleset_scope_name = String::from("__@ruleset_") + ruleset_name; + let ruleset_scope = Rc::new(RefCell::new(Scope::new(&ruleset_scope_name))); + ruleset_scope.borrow_mut().set_parent(Rc::downgrade(&parent_scope)); + + // + Ok(RuleSetIR::new()) +} + +fn generate_rule_expr_ir( + rule_expr: &RuleExpr, + parent_scope: &RcRefCellScope, +) -> IResult { + let rule_name = &*rule_expr.name.name; + + // Introduce a new scope for rule clause + let rule_scope_name = String::from("__@rule_") + rule_name; + let rule_scope = Rc::new(RefCell::new(Scope::new(&rule_scope_name))); + rule_scope.borrow_mut().set_parent(Rc::downgrade(&parent_scope)); + + + Ok(RuleSetIR::new()) +} diff --git a/rula-exec/src/generator/scope.rs b/rula-exec/src/generator/scope.rs new file mode 100644 index 0000000..2bfe616 --- /dev/null +++ b/rula-exec/src/generator/scope.rs @@ -0,0 +1,103 @@ +use std::collections::HashMap; + +use super::IResult; +use super::error::CompileError; +use super::symbol_table::SymbolInfo; +use std::cell::RefCell; +use std::rc::{Rc, Weak}; + +type SymbolName = String; + +// Scope Tree Node can only have one parent and could have multiple children +#[derive(Debug)] +pub struct Scope { + pub root: bool, + pub scope_name: String, + pub symbols: HashMap, + // child scope cannot make changes on parent scope + pub parent: Option>>, + // child scope might be changed + pub children: RefCell>>>, +} + +impl Scope { + pub fn new(scope_name: &str) -> Self { + Scope { + root: false, + // Default scope name set in the first node + scope_name: scope_name.to_string(), + symbols: HashMap::new(), + parent: None, + children: RefCell::new(vec![]), + } + } + + pub fn set_root(&mut self) { + match &self.parent { + Some(_parent) => { + panic!("A scope that has parent scope cannot be a root scope") + } + None => { + self.root = true; + } + } + } + + pub fn add_child(&self, child: Rc>) -> IResult<()> { + // If there is no parent scope found, return error + match &child.borrow_mut().parent { + // Parent information must be set before added + Some(_parent) => { /*found parent scope */ } + None => return Err(CompileError::ParentScopeNotFoundError), + } + self.children.borrow_mut().push(child); + Ok(()) + } + + pub fn set_parent(&mut self, parent: Weak>) { + self.parent = Some(parent); + } + + pub fn ref_parent(&self) -> IResult>>> { + match &self.parent { + Some(parent_ref) => { + // check if the parent is still alive + match parent_ref.upgrade() { + Some(parent) => Ok(Some(parent)), + None => Err(CompileError::ParentScopeDroppedError), + } + } + // Root node + None => Ok(None), + } + } + + pub fn add_symbol(&mut self, symbol_name: &str, symbol_info: SymbolInfo) { + self.symbols.insert(symbol_name.to_string(), symbol_info); + } + + pub fn get_symbol_info(&self, symbol_name: &str) -> Option { + // found in the current scope + match self.symbols.get(symbol_name) { + Some(symbol_info) => Some(symbol_info.clone()), + None => { + // get parent node + // FIXME: Don't wanna clone + match self.ref_parent().unwrap() { + Some(parent_node) => { + let symbol = parent_node + .borrow() + .get_symbol_info(symbol_name) + .expect("Failed to find the symbol") + .clone(); + return Some(symbol); + } + None => { + // No more parent here + return None; + } + } + } + } + } +} \ No newline at end of file diff --git a/rula-exec/src/generator/symbol_table.rs b/rula-exec/src/generator/symbol_table.rs new file mode 100644 index 0000000..deb0225 --- /dev/null +++ b/rula-exec/src/generator/symbol_table.rs @@ -0,0 +1,80 @@ +use super::scope::Scope; +use super::types::Types; + +#[derive(Debug)] +pub struct SymbolTable { + // scope hierarchy that can be climbed up to global scope + pub scope_tree: Box, +} + +impl SymbolTable { + pub fn new() -> Self { + SymbolTable { + // default scope name "__@" will be replaced by global scope "__@global" + scope_tree: Box::new(Scope::new("__@")), + } + } +} + +// For one symbol +#[derive(Debug, Clone)] +pub struct SymbolInfo { + // Data type used in RuLa + pub data_type: Types, +} + +impl SymbolInfo { + pub fn new() -> Self { + SymbolInfo { + data_type: Types::Unknown, + } + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + use std::cell::RefCell; + use super::*; + + #[test] + fn test_scope_tree_creation() { + // parent scope + let parent = Rc::new(RefCell::new(Scope::new("__@parent"))); + // child scope + let child = Rc::new(RefCell::new(Scope::new("__@child"))); + // register parent scope + child.borrow_mut().set_parent(Rc::downgrade(&parent)); + + // Check if it's possible to refer to the parent from child + let parent_ref = match child.borrow().ref_parent().unwrap() { + Some(paref) => paref, + None => { + panic!("No parents found"); + } + }; + assert_eq!(parent_ref.borrow().scope_name, String::from("__@parent")); + } + + #[test] + fn test_scope_tree_look_up() { + // climb up to root to find a value + // root scope + let root = Rc::new(RefCell::new(Scope::new("__@root"))); + root.borrow_mut() + .add_symbol("root_symbol", SymbolInfo::new()); + // child scope + let child = Rc::new(RefCell::new(Scope::new("__@child"))); + // grandchild + let grand_child = Rc::new(RefCell::new(Scope::new("__@grand_child"))); + + grand_child.borrow_mut().set_parent(Rc::downgrade(&child)); + child.borrow_mut().set_parent(Rc::downgrade(&root)); + + let root_symbol = child + .borrow_mut() + .get_symbol_info("root_symbol") + .expect("Failed to get symbol"); + assert_eq!(root_symbol.data_type, Types::Unknown); + } +} diff --git a/rula-exec/src/generator/types.rs b/rula-exec/src/generator/types.rs new file mode 100644 index 0000000..f934480 --- /dev/null +++ b/rula-exec/src/generator/types.rs @@ -0,0 +1,5 @@ + +#[derive(Debug, Clone, PartialEq)] +pub enum Types{ + Unknown +} \ No newline at end of file diff --git a/rula-exec/src/lib.rs b/rula-exec/src/lib.rs index cd71403..896a15f 100644 --- a/rula-exec/src/lib.rs +++ b/rula-exec/src/lib.rs @@ -2,7 +2,21 @@ extern crate quote; extern crate syn; +pub mod generator{ + pub mod generator; + pub mod symbol_table; + pub mod scope; + pub mod types; + + #[cfg(not(tarpaulin_include))] + mod error; + + use error::CompileError; + type IResult = std::result::Result; +} + // Generator of ruleset +#[deprecated] pub mod ruleset_gen { #[cfg(not(tarpaulin_include))] mod error; @@ -12,14 +26,17 @@ pub mod ruleset_gen { pub mod conf_parser; pub mod factory; pub mod ruleset; + // ruleset_generator generates RuleSet in json format pub mod ruleset_generator; pub mod tracker; pub mod types; mod test_ruleset_generator; + use error::InternalError; use error::RuleSetGenError; type IResult = std::result::Result; + type InternalResult = std::result::Result; } #[cfg(test)] diff --git a/rula-exec/src/ruleset_gen/action.rs b/rula-exec/src/ruleset_gen/action.rs index 70f4e63..51f074f 100644 --- a/rula-exec/src/ruleset_gen/action.rs +++ b/rula-exec/src/ruleset_gen/action.rs @@ -1,4 +1,4 @@ -use super::{ruleset::ProtocolMessages, types::RuLaResult}; +use super::ruleset::ProtocolMessages; use core::time::Duration; use serde::{Deserialize, Serialize}; diff --git a/rula-exec/src/ruleset_gen/condition.rs b/rula-exec/src/ruleset_gen/condition.rs index d5f44e0..44f3955 100644 --- a/rula-exec/src/ruleset_gen/condition.rs +++ b/rula-exec/src/ruleset_gen/condition.rs @@ -1,6 +1,6 @@ use super::ruleset::{PartnerAddr, ProtocolMessages}; -use serde::{Deserialize, Serialize}; use crate::ruleset_gen::ruleset::AddressKind; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct Condition { diff --git a/rula-exec/src/ruleset_gen/conf_parser.rs b/rula-exec/src/ruleset_gen/conf_parser.rs index 6ed16f9..0d3818d 100644 --- a/rula-exec/src/ruleset_gen/conf_parser.rs +++ b/rula-exec/src/ruleset_gen/conf_parser.rs @@ -1,6 +1,6 @@ -use crate::ruleset_gen::ruleset::AddressKind; use super::types::Repeater; use super::IResult; +use crate::ruleset_gen::ruleset::AddressKind; use serde::{Deserialize, Serialize}; @@ -24,7 +24,7 @@ pub fn parse_config(file_path: &PathBuf) -> IResult> { // Initiator (#1) < --- > Responder (#fin) // 1. Generate all repeaters let mut repeaters = vec![]; - for (index, content) in config.repeaters.iter().enumerate(){ + for (index, content) in config.repeaters.iter().enumerate() { let mut repeater = Repeater::new(&content.name); repeater.update_index(index as u64); repeater.update_address(content.address.clone()); @@ -60,8 +60,8 @@ fn deserialize_config(file_path: &PathBuf) -> IResult { #[cfg(test)] mod tests { use super::*; - use std::cell::RefCell; use crate::ruleset_gen::tracker::Tracker; + use std::cell::RefCell; #[test] fn test_parse_config() { @@ -77,7 +77,7 @@ mod tests { fn test_repeater_resolve() { // #1 < -- > #2 < -- > #3 < -- > #4 < -- > #5 let config_path = PathBuf::from("../examples/v2/config.json"); - let tracker = RefCell::new(Tracker::new()); + let _tracker = RefCell::new(Tracker::new()); let repeater_list = parse_config(&config_path).unwrap(); assert_eq!(repeater_list.len(), 5); diff --git a/rula-exec/src/ruleset_gen/error.rs b/rula-exec/src/ruleset_gen/error.rs index cd5ba0e..a721ce5 100644 --- a/rula-exec/src/ruleset_gen/error.rs +++ b/rula-exec/src/ruleset_gen/error.rs @@ -10,3 +10,10 @@ pub enum RuleSetGenError { ArgumentNumberError, NeedIdentifierTypeAnnotationError, } + +#[derive(Debug)] +pub enum InternalError { + ParentScopeNotFoundError, + ParentScopeDroppedError, + NoSymbolFoundError, +} diff --git a/rula-exec/src/ruleset_gen/ruleset_generator.rs b/rula-exec/src/ruleset_gen/ruleset_generator.rs index c929950..534989f 100644 --- a/rula-exec/src/ruleset_gen/ruleset_generator.rs +++ b/rula-exec/src/ruleset_gen/ruleset_generator.rs @@ -65,10 +65,11 @@ pub fn generate(ast_tree: &AstNode, config_path: PathBuf) -> IResult { generate_term_expr(term, tracker, scope, in_ruledef).unwrap() } - RepeaterCallArg::Ident(ident) => (generate_ident(ident, tracker, scope).unwrap()), - RepeaterCallArg::IntegerLit(int_lit) => (generate_int_lit(int_lit).unwrap()), + RepeaterCallArg::Ident(ident) => generate_ident(ident, tracker, scope).unwrap(), + RepeaterCallArg::IntegerLit(int_lit) => generate_int_lit(int_lit).unwrap(), RepeaterCallArg::PlaceHolder => return Err(RuleSetGenError::InitializationError), }; Ok((quote!(__factory.borrow().repeaters.at(#arg)), quote!(#arg))) diff --git a/rula-exec/src/ruleset_gen/test_ruleset_generator.rs b/rula-exec/src/ruleset_gen/test_ruleset_generator.rs index 9a785e1..e0ca582 100644 --- a/rula-exec/src/ruleset_gen/test_ruleset_generator.rs +++ b/rula-exec/src/ruleset_gen/test_ruleset_generator.rs @@ -109,7 +109,6 @@ mod tests { #[cfg(test)] mod term_expr_tests { - use super::*; #[test] fn simple_term_expr() { diff --git a/rula-exec/src/ruleset_gen/tracker.rs b/rula-exec/src/ruleset_gen/tracker.rs index c58e606..c8dc86b 100644 --- a/rula-exec/src/ruleset_gen/tracker.rs +++ b/rula-exec/src/ruleset_gen/tracker.rs @@ -1,13 +1,6 @@ -use serde_json::Value; - -use super::ruleset::{RuleSet, Stage}; -use super::ruleset_generator::{Scope, ValueTracker}; -use super::types::{Repeater, RuLaValue, Types}; -use std::cell::RefCell; +use super::types::Types; use std::collections::{HashMap, HashSet}; -use std::rc::Rc; -type NodeNumber = usize; type RuleName = String; // Track all global state in generation diff --git a/rula-exec/src/ruleset_gen/types.rs b/rula-exec/src/ruleset_gen/types.rs index 485b182..3aef374 100644 --- a/rula-exec/src/ruleset_gen/types.rs +++ b/rula-exec/src/ruleset_gen/types.rs @@ -1,8 +1,8 @@ use super::condition::*; use super::ruleset::Rule; +use crate::ruleset_gen::ruleset::AddressKind; use std::cell::RefCell; use std::rc::Rc; -use crate::ruleset_gen::ruleset::AddressKind; // Possible Type values in RuLa #[derive(Debug, Clone, PartialEq)] @@ -45,7 +45,7 @@ pub enum RuLaValue { } impl RuLaValue { - pub fn wrap(val: &T) -> Self { + pub fn wrap(_val: &T) -> Self { match std::any::type_name::() { // "Repeater" => {RuLaValue::Repeater(val as Repeater)} _ => todo!(), @@ -115,7 +115,7 @@ pub type RuleVec = Rc>>>; pub struct Repeater { // A readable name of this repeater pub name: String, - // An index of this repeater. + // An index of this repeater. // This is just for internal use to identify which repeater is which. pub index: u64, // A public address exposed to outer world @@ -137,11 +137,11 @@ impl Repeater { } } - pub fn update_index(&mut self, new_index: u64){ + pub fn update_index(&mut self, new_index: u64) { self.index = new_index } - pub fn update_address(&mut self, address: AddressKind){ + pub fn update_address(&mut self, address: AddressKind) { self.address = address } diff --git a/rula-exec/tests/ir_gen_test.rs b/rula-exec/tests/ir_gen_test.rs new file mode 100644 index 0000000..4874a79 --- /dev/null +++ b/rula-exec/tests/ir_gen_test.rs @@ -0,0 +1,17 @@ +// use rula_exec::ruleset_gen::generator::*; + +// #[cfg(test)] +// mod test_generate_rula_ir { +// use rula_parser::parser::ast::*; + +// use super::*; + +// #[test] +// fn test_rula_ir() { +// let rula = RuLa::new(RuLaKind::Ignore); +// // let test = generate_rula_ir(&rula).unwrap(); +// // for ir in test.iter(){ +// // println!("{:#?}", ir); +// // } +// } +// } diff --git a/rula-lib/src/lib.rs b/rula-lib/src/lib.rs index c25dfce..77c1590 100644 --- a/rula-lib/src/lib.rs +++ b/rula-lib/src/lib.rs @@ -5,5 +5,4 @@ pub mod time; // use ruleset::action::v2::ActionClauses; // use ruleset::ruleset::Rule; -use std::cell::RefCell; use std::rc::Rc; diff --git a/rula-lib/src/operation.rs b/rula-lib/src/operation.rs index 7521118..c2a1e65 100644 --- a/rula-lib/src/operation.rs +++ b/rula-lib/src/operation.rs @@ -1,5 +1,3 @@ -use std::borrow::BorrowMut; - use super::*; // use crate::qubit::QubitInterface; use rula_exec::ruleset_gen::{ diff --git a/rula-lib/src/prelude.rs b/rula-lib/src/prelude.rs index 89964a5..4599e78 100644 --- a/rula-lib/src/prelude.rs +++ b/rula-lib/src/prelude.rs @@ -2,8 +2,6 @@ use rula_exec::ruleset_gen::action::*; use rula_exec::ruleset_gen::condition::*; use rula_exec::ruleset_gen::ruleset::*; use rula_exec::ruleset_gen::types::*; -use std::cell::RefCell; -use std::collections::HashSet; pub fn res( rules: RuleVec, @@ -26,6 +24,8 @@ pub fn res( Qubit::new(qubit_index.clone()) } +pub fn __res__() {} + pub fn free(rules: RuleVec, qubit: &Qubit) { for rule in rules.borrow().iter() { rule.borrow_mut() @@ -35,7 +35,7 @@ pub fn free(rules: RuleVec, qubit: &Qubit) { } } -pub fn check_timer(rules: RuleVec, timer_id: String) { +pub fn check_timer(rules: RuleVec, _timer_id: String) { for rule in rules.borrow().iter() { rule.borrow_mut() .add_condition_clause(ConditionClauses::Timer(10.0)); @@ -43,11 +43,11 @@ pub fn check_timer(rules: RuleVec, timer_id: String) { todo!("Need fix"); } -pub fn cmp(rules: RuleVec) { +pub fn cmp(_rules: RuleVec) { todo!("Add!") } -pub fn set_timer(rules: RuleVec) { +pub fn set_timer(_rules: RuleVec) { todo!("Add timer"); } @@ -82,7 +82,9 @@ pub fn wait( pub fn recv(rules: RuleVec, source_repeater: &Repeater) -> Message { for rule in rules.borrow().iter() { rule.borrow_mut() - .add_condition_clause(ConditionClauses::Recv(Recv::new(source_repeater.address.clone()))); + .add_condition_clause(ConditionClauses::Recv(Recv::new( + source_repeater.address.clone(), + ))); } let mut new_message = Message::place_holder(); new_message.update_source(source_repeater.address.clone()); diff --git a/rula-parser/Cargo.toml b/rula-parser/Cargo.toml index 0214f96..d01171a 100644 --- a/rula-parser/Cargo.toml +++ b/rula-parser/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rula_parser" +name = "rula-parser" version = "0.1.0" edition = "2021" description = "A parser for RuLa programming language" diff --git a/rula-parser/src/lib.rs b/rula-parser/src/lib.rs index 8afc0bd..e1117d4 100644 --- a/rula-parser/src/lib.rs +++ b/rula-parser/src/lib.rs @@ -7,7 +7,6 @@ extern crate pest_derive; #[grammar = "parser/rula.pest"] pub struct RuLaParser; -use once_cell::sync::OnceCell; use std::path::PathBuf; use parser::ast::AstNode; @@ -15,8 +14,6 @@ use parser::error::RuLaError; use parser::IResult; use pest::Parser; -use std::fs; - use crate::parser::build_ast_from_rula; // Interface function to parse rula code diff --git a/rula-parser/src/parser.rs b/rula-parser/src/parser.rs index e6bbc19..028f0bf 100644 --- a/rula-parser/src/parser.rs +++ b/rula-parser/src/parser.rs @@ -2,8 +2,6 @@ pub mod ast; pub mod error; mod util; -use std::env; - use crate::{parse, Rule}; // RuLa use ast::*; @@ -303,7 +301,7 @@ fn extract_rule( } Ok(target_rule) } - _ => return panic!("Program Kind?"), + _ => unreachable!(), } } AstNode::PlaceHolder => panic!("AST does not contain RuLa AST node"), diff --git a/rula-parser/src/parser/ast.rs b/rula-parser/src/parser/ast.rs index 11188b9..df0efee 100644 --- a/rula-parser/src/parser/ast.rs +++ b/rula-parser/src/parser/ast.rs @@ -1,4 +1,3 @@ -use std::iter::Iterator; use std::path::PathBuf; /** diff --git a/rula/Cargo.toml b/rula/Cargo.toml index 6d378a0..708e55c 100644 --- a/rula/Cargo.toml +++ b/rula/Cargo.toml @@ -11,5 +11,5 @@ clap = { version = "4.0.10", features = ["derive"] } toml = "0.5.9" proc-macro2 = "1.0" # serde = "1.0" -rula_parser = {path="../rula-parser"} +rula-parser = {path="../rula-parser"} rula-exec = {path="../rula-exec"} diff --git a/rula/src/main.rs b/rula/src/main.rs index c4140f0..0c540fc 100644 --- a/rula/src/main.rs +++ b/rula/src/main.rs @@ -1,9 +1,9 @@ use proc_macro2::TokenStream; +use std::env; use std::fs::File; use std::io::{Read, Write}; use std::path::PathBuf; use std::process::Command; -use std::{env, fs}; use clap::Parser; @@ -21,6 +21,9 @@ struct Args { /// RuLa configuration config: PathBuf, + /// Output format of the RuleSet + output_format: Option, + /// Output directory path output_dir_path: Option, diff --git a/ruleset-ir/Cargo.toml b/ruleset-ir/Cargo.toml new file mode 100644 index 0000000..749878e --- /dev/null +++ b/ruleset-ir/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ruleset-ir" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/ruleset-ir/src/instructions.rs b/ruleset-ir/src/instructions.rs new file mode 100644 index 0000000..d277899 --- /dev/null +++ b/ruleset-ir/src/instructions.rs @@ -0,0 +1,93 @@ +use crate::inst; +use crate::types::*; +use crate::InstructionInfo; +use crate::RSIR; + +// These inst!uctions are expanded to the structures +// The inst!uction named with "_DUP" will be renamed into a name without "_DUP" for QuISP +// READ and WRITE are just annotations to know which arguments are written or read + +// arithmetic +inst!(ADD, WRITE reg0: RegId, READ reg1: RegId, READ val: i64); +inst!(ADD_DUP, WRITE reg0: RegId, READ reg1: RegId, READ reg2: RegId); // In QuISP, this is the same as `Add` +inst!(SUB, WRITE reg0: RegId, READ reg1: RegId, READ val: i64); +inst!(SUB_DUP, WRITE reg0: RegId, READ reg1: RegId, READ reg2: RegId); // In QuISP, this is the same as `Sub` +inst!(INC, WR reg0: RegId); +inst!(SET, WRITE reg0: RegId, READ val: i64); +// arithmetic bitwise +inst!(BITWISE_AND, WRITE reg0: RegId, READ reg1: RegId, READ val: i64); +inst!(BITWISE_AND_DUP, WRITE reg0: RegId, READ reg1: RegId, READ reg2: RegId); // In QuISP, this is the same as `BITWISE_AND` +inst!(BITWISE_OR, WRITE reg0: RegId, READ reg1: RegId, READ val: i64); +inst!(BITWISE_OR_DUP, WRITE reg0: RegId, READ reg1: RegId, READ reg2: RegId); +inst!(BITWISE_XOR, WRITE reg0: RegId, READ reg1: RegId, READ reg2: RegId); +inst!(BITWISE_XOR_DUP, WRITE reg0: RegId, READ reg1: RegId, READ val: i64); +// in place operation +inst!(BITWISE_AND_INP, WRITE reg0: RegId, READ reg1: RegId ); // in-place operation: first_reg = first_reg | second_reg (bitwise and); +inst!(BITWISE_AND_INP_DUP, WRITE reg0: RegId , READ val: i64 ); // in-place operation: first_reg = first_reg | int (bitwise and); +inst!(BITWISE_OR_INP, WRITE reg0: RegId , READ reg1: RegId ); // in-place operation: first_reg = first_reg | second_reg (bitwise or); +inst!(BITWISE_OR_INP_DUP, WRITE reg0: RegId, READ val: i64 ); // in-place operation: first_reg = first_reg | int (bitwise or); +inst!(BITWISE_XOR_INP, WRITE reg0: RegId, READ reg1: RegId ); // in-place operation: first_reg = first_reg | second_reg (bitwise xor); +inst!(BITWISE_XOR_INP_DUP, WRITE reg0: RegId, READ reg1: i64 ); // in-place operation: first_reg = first_reg | int (bitwise xor); + +// control flow +inst!(BEQ, LABEL label: Label, READ reg0: RegId, READ reg1: RegId); // branch if the reg values are same +inst!(BEQ_DUP, LABEL label: Label, READ reg0: RegId, READ val: i64); // branch if the reg value is equal to the int value +inst!(BEZ, LABEL label: Label, READ reg0: RegId); // branch if the reg value is zero +inst!(BNZ, LABEL label: Label, READ reg0: RegId); // branch if the reg value is not zero +inst!(BLT, LABEL label: Label, READ reg0: RegId, READ val: i64); // branch if the reg value is less than the int value +inst!(BRANCH_IF_LOCKED, LABEL label: Label, READ reg: RegId); +inst!(BRANCH_IF_QUBIT_FOUND, LABEL label: Label); +inst!(BRANCH_IF_MESSAGE_FOUND, LABEL label: Label); +inst!(JMP, LABEL label: Label); +inst!(ERROR, message: String); // stop execution and show error +inst!(RET, code: ReturnCode); // stop execution with the ReturnCode + +// memory operations +inst!(LOAD, WRITE reg0: RegId, READ mem_key: MemoryKey); +inst!(STORE, WRITE mem_key: MemoryKey, READ reg0: RegId); +inst!(STORE_DUP, WRITE mem_key: MemoryKey, READ val: i64); + +inst!(LOAD_LEFT_OP, WRITE reg0: RegId, READ mem_key: MemoryKey); // READ memkey? +inst!(LOAD_RIGHT_OP, WRITE reg0: RegId, READ mem_key: MemoryKey); + +// qubit retrieval operations +inst!(GET_QUBIT, WRITE qubit0: QubitId, READ qnode_addr0: QNodeAddr, READ val: i64); // may throw "no qubit error" +inst!(GET_QUBIT_R_DUP, WRITE qubit0: QubitId, READ partner_addr: QNodeAddr, READ reg0: RegId); // may throw "no qubit error" +inst!(GET_QUBIT_RQ_DUP, WRITE reg0: RegId, READ partner_addr: QNodeAddr, READ qubit_index: RegId); +inst!(GET_QUBIT_BY_SEQ_NO, WRITE reg0: RegId, READ partner_addr: QNodeAddr, READ sequence_number: RegId); +inst!(GET_QUBIT_BY_SEQ_NO_DUP, WRITE qubit: QubitId, READ partner_addr: QNodeAddr, READ sequence_number: RegId); + +// qubit quantum gate operations +inst!(MEASURE_RANDOM, WRITE reg0: MemoryKey, READ target_qubit: QubitId); +inst!(MEASURE, WRITE reg0: MemoryKey, READ target_qubit: QubitId, READ meas_basis: Basis); +inst!(MEASURE_R_DUP, WRITE reg0: RegId, READ target_qubit: QubitId, READ meas_basis: Basis); +inst!(MEASURE_RQ_DUP, WRITE reg0: RegId, READ bit: i64, READ target_qubit: QubitId, READ meas_basis: Basis); // this will store the result at index (bitset); of the RegId specified by the int +inst!(GATE_X, READ target_qubit: QubitId); +inst!(GATE_Z, READ target_qubit: QubitId); +inst!(GATE_Y, READ target_qubit: QubitId); +inst!(GATE_CNOT, READ control_qubit: QubitId, READ target_qubit: QubitId); +// circuit operations +inst!(PURIFY_X, WRITE reg0: RegId, READ val: i64, READ keep_qubit_id: QubitId, READ trash_qubit_id: QubitId); +inst!(PURIFY_Z, WRITE reg0: RegId, READ val: i64, READ keep_qubit_id: QubitId, READ trash_qubit_id: QubitId); +inst!(PURIFY_Y, WRITE reg0: RegId, READ val: i64, READ keep_qubit_id: QubitId, READ trash_qubit_id: QubitId); + +// resource management operations +// inst!uctions we would want later: crucial for entanglement pumping, banding, and multipartite states +// inst!(SET_NAME, QubitId, {new_name: string | RegId }); // when using partner as name is not enough +inst!(FREE_QUBIT, READ qubit: QubitId); +inst!(PROMOTE, READ qubit: QubitId); +inst!(PROMOTE_DUP, READ qubit: QubitId, READ reg0: RegId); // promote with new partner/new name +inst!(LOCK_QUBIT, READ qubit: QubitId, READ reg0_action_index: RegId); + +// message operations +inst!(GET_MESSAGE_SEQ, WRITE sequence_number: RegId, READ message_index: RegId); +inst!(COUNT_MESSAGE, WRITE count: RegId, READ sequence_number: RegId); +inst!(GET_MESSAGE, WRITE reg_content: RegId, READ sequence_number: RegId, READ message_index: i64); // for purification [result] +inst!(GET_MESSAGE_DUP, WRITE content0: RegId, WRITE content1: RegId, READ sequence_number: RegId, READ message_index: i64); // for swapping [correction_op, new_partner] +inst!(DELETE_MESSAGE, READ sequence_number: RegId); // delete all messages with this sequence number + +// send classical messages +inst!(SEND_LINK_TOMOGRAPHY_RESULT, READ partner_addr: QNodeAddr, READ reg0: RegId, READ mem_key: MemoryKey, READ val: i64, READ time: Time); // partner addr, current count reg_id, outcome key, max_count, start_time +inst!(SEND_PURIFICATION_RESULT, READ partner_addr: QNodeAddr, READ reg0: RegId /* measurement_result encoded in int */, READ reg1: RegId /* sequence_number */, READ pur_type: PurType); +inst!(SEND_SWAPPING_RESULT, READ partner_addr: QNodeAddr /* receipient */, READ reg0: RegId /* pauli_op */, READ new_partner_addr: QNodeAddr /* new partner*/, READ sequence_number: RegId /* sequence_number */); +inst!(NOP, READ nop: Option); diff --git a/ruleset-ir/src/lib.rs b/ruleset-ir/src/lib.rs new file mode 100644 index 0000000..6ca5fcd --- /dev/null +++ b/ruleset-ir/src/lib.rs @@ -0,0 +1,207 @@ +use std::fmt::Debug; + +#[allow(dead_code)] +#[allow(non_camel_case_types)] +pub mod instructions; + +pub mod types; + +// A structure that stores a set of instructions defined in instructions +#[derive(Debug)] +pub struct RuleSetIR { + instructions: Vec>, +} + +impl RuleSetIR { + pub fn new() -> Self { + RuleSetIR { + instructions: vec![], + } + } + + pub fn add_instruction(&mut self, instruction: Box) { + self.instructions.push(instruction) + } + + pub fn merge(&mut self, ruleset_ir: &mut RuleSetIR) { + // read rulesets and push back one by one + self.instructions.append(&mut ruleset_ir.instructions); + } + + pub fn export_ir(&self) -> String { + let mut ruleset_ir = String::from(""); + for inst in self.instructions.iter() { + ruleset_ir = ruleset_ir + &inst.export() + "\n"; + } + ruleset_ir + } + + pub fn import_ir(&self, _ruleset_ir: String) {} +} + +// All the ruleset instructions needs to implement this trait RuleSet IR (RSIR). +// This trait implements `gen_ir` that returns a text format instruction for RuleSet +pub trait RSIR { + fn info(&self) -> InstructionInfo; + fn export(&self) -> String; + fn import(&self, ir: String); +} + +impl Debug for dyn RSIR { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let instruction_info = self.info(); + f.debug_struct("InstructionInfo") + .field("instruction name", &instruction_info.name) + .finish() + } +} + +// For debugging (Add arguments and useful information later) +pub struct InstructionInfo { + name: String, +} + +#[macro_export] +macro_rules! inst { + // with annotation + ($inst_name: ident, $($mode: ident $args: ident: $typedef: ty),* ) => { + #[derive(Debug)] + pub struct $inst_name { + instruction_name: String, + $( + $args: $typedef, + )* + } + + impl $inst_name { + #[allow(dead_code)] + pub fn new($($args: $typedef),*) -> Self { + $inst_name { + instruction_name: stringify!($inst_name).to_string(), + $( + $args: $args, + )* + } + } + } + + impl RSIR for $inst_name { + fn info(&self) -> InstructionInfo{ + InstructionInfo{ + name: self.instruction_name.clone() + } + } + + fn export(&self) -> String{ + String::from("") + } + + fn import(&self, _ir: String){ + todo!("IR importing is under construction") + } + + } + }; + // without annotation (need refactor) + ($inst_name: ident, $($args: ident: $typedef: ty),* ) => { + #[derive(Debug)] + pub struct $inst_name { + instruction_name: String, + $( + $args: $typedef, + )* + } + + impl $inst_name { + #[allow(dead_code)] + pub fn new($($args: $typedef),*) -> Self { + $inst_name { + instruction_name: stringify!($inst_name).to_string(), + $( + $args: $args, + )* + } + } + } + + impl RSIR for $inst_name { + fn info(&self) -> InstructionInfo{ + InstructionInfo{ + name: self.instruction_name.clone() + } + } + + fn export(&self) -> String{ + String::from("") + } + + fn import(&self, _ir: String){ + todo!("IR importing is under construction") + } + + } + }; +} + +// this function is to see the contents of inst! macro +#[allow(dead_code)] +fn expand() { + inst!(TestInstruction, READ test: String, WRITE test2: String); +} + +#[cfg(test)] +mod tests { + use crate::{ + instructions::{ADD, BITWISE_AND, RET, SET}, + types::{RegId, ReturnCode}, + RuleSetIR, + }; + + #[test] + fn test_ruleset_ir_merging() { + let mut parent_ir = RuleSetIR::new(); + // SET instruction + let set_inst_0 = SET::new(RegId::Reg0, 0); + let set_inst_1 = SET::new(RegId::Reg1, 0); + // ADD instruction + let add_inst = ADD::new(RegId::Reg0, RegId::Reg1, 1); + parent_ir.add_instruction(Box::new(set_inst_0)); + parent_ir.add_instruction(Box::new(set_inst_1)); + parent_ir.add_instruction(Box::new(add_inst)); + let mut child_ir = RuleSetIR::new(); + let bit_wise_and_inst = BITWISE_AND::new(RegId::Reg0, RegId::Reg1, 0); + child_ir.add_instruction(Box::new(bit_wise_and_inst)); + + parent_ir.merge(&mut child_ir); + + assert_eq!(parent_ir.instructions.len(), 4); + println!("{:#?}", parent_ir.instructions); + } + + #[test] + fn test_merging_multiple_ruleset_ir() { + let mut parent_ir = RuleSetIR::new(); + // RET instruction + let ret_inst = RET::new(ReturnCode::Nothing); + parent_ir.add_instruction(Box::new(ret_inst)); + + let mut child_ir1 = RuleSetIR::new(); + child_ir1.add_instruction(Box::new(ADD::new(RegId::Reg0, RegId::Reg1, 1))); + + let mut child_ir2 = RuleSetIR::new(); + child_ir2.add_instruction(Box::new(ADD::new(RegId::Reg0, RegId::Reg1, 1))); + + let mut child_ir3 = RuleSetIR::new(); + child_ir3.add_instruction(Box::new(ADD::new(RegId::Reg1, RegId::Reg2, 1))); + + let ruleset_irs = vec![child_ir1, child_ir2, child_ir3]; + + for mut ir in ruleset_irs.into_iter() { + parent_ir.merge(&mut ir); + } + + // The number of instrucitons must be 4 () + assert_eq!(parent_ir.instructions.len(), 4); + println!("{:#?}", parent_ir.instructions); + } +} diff --git a/ruleset-ir/src/types.rs b/ruleset-ir/src/types.rs new file mode 100644 index 0000000..9ff0362 --- /dev/null +++ b/ruleset-ir/src/types.rs @@ -0,0 +1,56 @@ +#[derive(Debug)] +pub enum RegId { + Reg0, // Register 0 + Reg1, + Reg2, + Reg3, + Reg4, +} + +#[derive(Debug)] +pub struct Label { + _val: String, +} + +#[derive(Debug)] +pub enum ReturnCode { + Nothing, + CondFailed, + CondPassed, + RsTerminated, + Error, +} + +#[derive(Debug)] +pub struct MemoryKey { + _key: String, +} + +#[derive(Debug)] +pub struct QNodeAddr { + _val: i64, +} + +#[derive(Debug, Hash)] +pub struct QubitId { + _val: i64, +} + +#[derive(Debug)] +pub enum Basis { + Z, + Y, + X, + Random, +} + +// Used in ver.1 Ruleset +// This must be compatible with omnetpp simtime. +#[derive(Debug)] +pub struct Time; + +#[derive(Debug)] +pub struct PurType; + +#[derive(Debug)] +pub struct Nop; diff --git a/tests-e2e/Cargo.toml b/tests-e2e/Cargo.toml index d98108e..0343910 100644 --- a/tests-e2e/Cargo.toml +++ b/tests-e2e/Cargo.toml @@ -10,12 +10,11 @@ edition = "2021" [dependencies] rula = {path = "../rula"} -rula_parser = { path = "../rula-parser" } +rula-parser = { path = "../rula-parser" } rula-exec = {path="../rula-exec"} rula-lib = {path="../rula-lib"} once_cell = "1.15.0" -itertools = "0.10.5" proc-macro2 = "1.0" futures = "0.3.25" toml = "0.5.9" diff --git a/tests-e2e/tests/e2e_rulesetgen_test.rs b/tests-e2e/tests/e2e_rulesetgen_test.rs index ba59db8..07eab2a 100644 --- a/tests-e2e/tests/e2e_rulesetgen_test.rs +++ b/tests-e2e/tests/e2e_rulesetgen_test.rs @@ -10,7 +10,7 @@ use std::path::PathBuf; use std::process::Command; // Helper functions for creating Rules -pub(crate) fn create_rule( +pub(crate) fn _create_rule( name: &str, id: u32, shared_tag: u32,