diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index daeef29..2611018 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,8 +2,8 @@ image: "rust:latest" test:cargo: script: - - rustup default nightly-2022-03-27 + - rustup default nightly-2022-08-25 - rustup show - rustc --version && cargo --version - rustup component add rust-src - - cargo test --all \ No newline at end of file + - cargo build --release \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a26e32e..1cea3e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,38 @@ [package] name = "mech-program" -version = "0.0.5" +version = "0.1.0" authors = ["Corey Montella "] description = "Organizes Mech cores into a coordinated program. Handles reading files, interfacing with libraries, and persisting changes top disk." documentation = "http://docs.mech-lang.org" -homepage = "http://mech-lang.org" +homepage = "https://mech-lang.org" repository = "https://github.com/mech-lang/program" keywords = ["mech", "language", "programming", "dataflow", "runtime"] categories = ["science::robotics", "science", "game-engines", "web-programming"] license = "Apache-2.0" readme = "README.md" -edition = "2018" +edition = "2021" [badges] gitlab = { repository = "mech-lang/program", branch = "main" } maintenance = { status = "actively-developed" } [dependencies] -mech-core = {git = "https://gitlab.com/mech-lang/core"} -mech-syntax = {git = "https://gitlab.com/mech-lang/syntax"} -mech-utilities = {git = "https://gitlab.com/mech-lang/utilities"} +mech-core = {git = "https://gitlab.com/mech-lang/core", branch = "v0.1-beta"} +mech-syntax = {git = "https://gitlab.com/mech-lang/syntax", branch = "v0.1-beta"} +mech-utilities = {git = "https://gitlab.com/mech-lang/utilities", branch = "v0.1-beta"} -time = "0.2.25" -serde = "1.0.123" -serde_derive = "1.0.123" -serde_json = "1.0.62" -bincode = "1.3.1" -libloading = "0.7.3" +time = "0.3.17" +serde = "1.0.152" +serde_derive = "1.0.152" +serde_json = "1.0.91" +bincode = "1.3.3" +libloading = "0.7.4" lazy_static = "1.4.0" reqwest = "0.9.22" colored = "2.0.0" -crossbeam-channel = "0.5.1" -url = "2.2.1" -hashbrown = "0.12.1" -websocket = "0.26.2" -miniz_oxide = "0.4.4" -indexmap = "1.7.0" \ No newline at end of file +crossbeam-channel = "0.5.6" +url = "2.3.1" +hashbrown = "0.13.1" +websocket = "0.26.5" +miniz_oxide = "0.6.2" +indexmap = "1.9.2" \ No newline at end of file diff --git a/README.md b/README.md index 942450b..d7215e0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@

- +

-Mech is a language for developing **data-driven**, **reactive** systems like animations, games, and robots. It makes **composing**, **transforming**, and **distributing** data easy, allowing you to focus on the essential complexity of your project. +Mech is a language for developing **data-driven**, **reactive** systems like robots, games, and animations. It makes **composing**, **transforming**, and **distributing** data easy, allowing you to focus on the essential complexity of your project. -Read about progress on our [blog](http://mech-lang.org/blog/), follow us on Twitter [@MechLang](https://twitter.com/MechLang), or join the mailing list: [talk@mech-lang.org](http://mech-lang.org/page/community/). +You can try Mech online at [https://mech-lang.org/try](https://mech-lang.org/try). + +Usage and installation instructions can be found in the [documentation](https://mech-lang.org/#/docs/index.mec) or the [main Mech repository](https://github.com/mech-lang/mech). + +Be sure to follow our [blog](https://mech-lang.org/blog/)([RSS](https://mech-lang.org/feed.xml))! ## Program @@ -12,16 +16,13 @@ Organizes Mech cores into a coordinated program. Handles reading files, interfac ## Contents -- `Program` - holds a Mech core and channels for communicating to a RunLoop. -- `RunLoop` - holds a handle to a thread on which a `ProgramRunner` is running. It also holds channels for communicating between the ProgramRunner and a client, like an editor or a REPL. -- `Persister` - reads from and writes transactions to *.mdb files. -- `ProgramRunner` - Starts an infinite run loop on a thread that continually processes messages received messages. +- **program** - holds a Mech core and channels for communicating to a RunLoop. +- **runloop** - holds a handle to a thread on which a Mech program is running. It also holds channels for communicating between and editor, REPL, or remote core. +- **persister** - reads from and writes transactions to *.blx files. ## Project Status -Mech is currently in the **alpha** stage of development. This means that while some features work and are tested, programs are still likely to crash and produce incorrect results. We've implemented some language features, but many are not yet implemented. - -Feel free to use the language for your own satisfaction, but please don't use it for anything important. +Mech is currently in the **beta** stage of development. This means that the language is at a suitable stage for a wider audience. While most language feature implementations are started, none are finished, and some new features may, while others could be removed. Backwards and forwards compatibility of Mech programs is not guaranteed at this time. ## License diff --git a/src/lib.rs b/src/lib.rs index 362a59f..2239b19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ extern crate time; extern crate mech_core; use mech_core::*; extern crate mech_syntax; +use mech_syntax::formatter::Formatter; extern crate mech_utilities; extern crate colored; extern crate websocket; @@ -51,7 +52,7 @@ pub use self::program::{Program}; pub use self::runloop::{ProgramRunner, RunLoop, ClientMessage}; pub use self::persister::{Persister}; -pub fn format_errors(errors: &Vec) -> String { +pub fn format_errors(errors: &Vec) -> String { let mut formatted_errors = "".to_string(); let plural = if errors.len() == 1 { "" @@ -61,15 +62,22 @@ pub fn format_errors(errors: &Vec) -> String { let error_notice = format!("🐛 Found {} Error{}:\n", &errors.len(), plural); formatted_errors = format!("{}\n{}\n\n", formatted_errors, error_notice); for error in errors { - formatted_errors = format!("{}{} {} {} {}\n\n", formatted_errors, "---".truecolor(246,192,78), "Block".truecolor(246,192,78), "BLOCKNAME", "--------------------------------------------".truecolor(246,192,78)); - formatted_errors = format!("{}\n{:?}\n", formatted_errors, error); - formatted_errors = format!("{}\n", formatted_errors); - formatted_errors = format!("{}\n{}",formatted_errors, "----------------------------------------------------------------\n\n".truecolor(246,192,78)); + formatted_errors = format!("{}{}\n\n", formatted_errors, "───────────────────────────────────────────────────────────────────".truecolor(246,192,78)); + match &error.kind { + MechErrorKind::ParserError(ast,report,msg) => { formatted_errors = format!("{}{}", formatted_errors, msg);} + MechErrorKind::MissingTable(table_id) => { + formatted_errors = format!("{} Missing table: {}\n", formatted_errors, error.msg); + } + _ => { + formatted_errors = format!("{}\n{:?}\n", formatted_errors, error); + } + } } + formatted_errors = format!("{}\n{}",formatted_errors, "───────────────────────────────────────────────────────────────────\n\n".truecolor(246,192,78)); formatted_errors } -pub fn download_machine(machine_name: &str, name: &str, path_str: &str, ver: &str, outgoing: Option>) -> Result> { +pub fn download_machine(machine_name: &str, name: &str, path_str: &str, ver: &str, outgoing: Option>) -> Result { create_dir("machines"); let machine_file_path = format!("machines/{}",machine_name); @@ -78,7 +86,7 @@ pub fn download_machine(machine_name: &str, name: &str, path_str: &str, ver: &st // Download from the web if path.to_str().unwrap().starts_with("https") { match outgoing { - Some(ref sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Downloading]".bright_cyan(), name, ver)));} + Some(ref sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Downloading]".truecolor(153,221,85), name, ver)));} None => (), } let machine_url = format!("{}/{}", path_str, machine_name); @@ -108,7 +116,7 @@ pub fn download_machine(machine_name: &str, name: &str, path_str: &str, ver: &st // Load from a local directory } else { match outgoing { - Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".bright_cyan(), name, ver)));} + Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".truecolor(153,221,85), name, ver)));} None => (), } let machine_path = format!("{}{}", path_str, machine_name); @@ -120,6 +128,8 @@ pub fn download_machine(machine_name: &str, name: &str, path_str: &str, ver: &st } let machine_file_path = format!("machines/{}",machine_name); let message = format!("Can't load library {:?}", machine_file_path); - let machine = unsafe{Library::new(machine_file_path).expect(&message)}; - Ok(machine) + match unsafe{Library::new(machine_file_path)} { + Ok(machine) => Ok(machine), + Err(err) => Err(MechError{msg: "".to_string(), id: 1273, kind: MechErrorKind::GenericError(format!("{:?}",message))}), + } } diff --git a/src/program.rs b/src/program.rs index 7eb2e07..906f117 100644 --- a/src/program.rs +++ b/src/program.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use std::rc::Rc; use std::cell::RefCell; use std::path::{Path, PathBuf}; +use std::time::{Instant}; use mech_core::*; use mech_syntax::compiler::Compiler; @@ -32,6 +33,7 @@ use libloading::Library; use std::io::copy; use std::io; use std::net::{SocketAddr, UdpSocket}; +use std::fmt; use time; @@ -87,8 +89,8 @@ pub struct Program { pub mech: Core, pub cores: HashMap, pub remote_cores: HashMap, - pub input_map: HashMap<(TableId,TableIndex,TableIndex),HashSet>, - pub libraries: HashMap, + pub input_map: HashMap<(TableId,RegisterIndex,RegisterIndex),HashSet>, + pub libraries: HashMap>, pub machines: HashMap>, pub mech_functions: HashMap>, pub machine_repository: HashMap, // (name, (version, url)) @@ -98,12 +100,13 @@ pub struct Program { pub errors: HashSet, programs: usize, loaded_machines: HashSet, - pub listeners: HashMap<(TableId,TableIndex,TableIndex),HashSet>, - pub trigger_to_listener: HashMap<(TableId,TableIndex,TableIndex),((TableId, TableIndex, TableIndex),HashSet)> + pub listeners: HashMap<(TableId,RegisterIndex,RegisterIndex),HashSet>, + pub trigger_to_listener: HashMap<(TableId,RegisterIndex,RegisterIndex),((TableId, RegisterIndex, RegisterIndex),HashSet)>, + pub registry: String, } impl Program { - pub fn new(name:&str, capacity: usize, recursion_limit: u64, outgoing: Sender, incoming: Receiver) -> Program { + pub fn new(name:&str, capacity: usize, recursion_limit: u64, outgoing: Sender, incoming: Receiver, registry: String) -> Program { let mut mech = Core::new(); Program { name: name.to_owned(), @@ -123,10 +126,11 @@ impl Program { programs: 0, listeners: HashMap::new(), trigger_to_listener: HashMap::new(), + registry, } } - pub fn trigger_machine(&mut self, register: &(TableId,TableIndex,TableIndex)) -> Result<(),MechError> { + pub fn trigger_machine(&mut self, register: &(TableId,RegisterIndex,RegisterIndex)) -> Result<(),MechError> { let (table_id,_,_) = register; match self.machines.get_mut(table_id.unwrap()) { Some(mut machine) => { @@ -134,22 +138,22 @@ impl Program { let table_ref_brrw = table_ref.borrow(); machine.on_change(&table_ref_brrw); }, - _ => (), // Warn user that the machine is not loaded? Or is it okay to just try? + _ => (), // Warn user that the machine is not loaded? Or is it okay to just try? } Ok(()) } - pub fn compile_program(&mut self, input: String) -> Result,MechError> { + pub fn compile_program(&mut self, input: String) -> Result,Vec,Vec))>,MechError> { let mut compiler = Compiler::new(); - let blocks = compiler.compile_str(&input.clone())?; - let (new_block_ids,block_errors) = self.mech.load_blocks(blocks); + let sections = compiler.compile_str(&input.clone())?; + let result = self.mech.load_sections(sections); //self.errors.append(&mut self.mech.runtime.errors.clone()); /*let mech_code = *MECH_CODE; self.programs += 1; let txn = vec![Change::Set((mech_code, vec![(TableIndex::Index(self.programs),TableIndex::Index(1),Value::from_str(&input.clone()))]))]; self.outgoing.send(RunLoopMessage::Transaction(txn));*/ - Ok(new_block_ids) + Ok(result) } pub fn compile_fragment(&mut self, input: String) { @@ -180,12 +184,12 @@ impl Program { Ok(mut file) => { // Loading machine_repository index match &outgoing { - Some(sender) => {sender.send(ClientMessage::String(format!("{} Machine registry.", "[Loading]".bright_cyan())));} - None => {return Err(MechError{id: 1244, kind: MechErrorKind::None});}, + Some(sender) => {sender.send(ClientMessage::String(format!("{} Machine registry.", "[Loading]".truecolor(153,221,85))));} + None => {return Err(MechError{msg: "".to_string(), id: 1244, kind: MechErrorKind::None});}, } let mut contents = String::new(); match file.read_to_string(&mut contents) { - Err(_) => {return Err(MechError{id: 1445, kind: MechErrorKind::None});}, + Err(_) => {return Err(MechError{msg: "".to_string(), id: 1445, kind: MechErrorKind::None});}, _ => (), } contents @@ -193,28 +197,30 @@ impl Program { Err(_) => { // Download machine_repository index match &outgoing { - Some(sender) => {sender.send(ClientMessage::String(format!("{} Updating machine registry.", "[Downloading]".bright_cyan())));} - None => {return Err(MechError{id: 1246, kind: MechErrorKind::None});}, + Some(sender) => {sender.send(ClientMessage::String(format!("{} Updating machine registry from:\n{}", "[Downloading]".truecolor(153,221,85),self.registry)));} + None => {return Err(MechError{msg: "".to_string(), id: 1246, kind: MechErrorKind::None});}, } // Download registry - let registry_url = "https://gitlab.com/mech-lang/machines/mech/-/raw/main/src/registry.mec"; + let registry_url = &self.registry; let mut response_text = match reqwest::get(registry_url) { Ok(mut response) => { match response.text() { - Ok(text) => text, - Err(_) => {return Err(MechError{id: 1235, kind: MechErrorKind::None});}, + Ok(text) => { + text + }, + Err(_) => {return Err(MechError{msg: "".to_string(), id: 1235, kind: MechErrorKind::None});}, } } - Err(_) => {return Err(MechError{id: 1236, kind: MechErrorKind::None});}, + Err(_) => {return Err(MechError{msg: "".to_string(), id: 1236, kind: MechErrorKind::None});}, }; // Save registry let mut dest = match File::create("machines/registry.mec") { Ok(dest) => dest, - Err(_) => {return Err(MechError{id: 1237, kind: MechErrorKind::None});}, + Err(_) => {return Err(MechError{msg: "".to_string(), id: 1237, kind: MechErrorKind::None});}, }; match dest.write_all(response_text.as_bytes()) { Ok(dest) => dest, - Err(_) => {return Err(MechError{id: 1238, kind: MechErrorKind::None});}, + Err(_) => {return Err(MechError{msg: "".to_string(), id: 1238, kind: MechErrorKind::None});}, } response_text } @@ -222,18 +228,18 @@ impl Program { // Compile machine registry let mut registry_compiler = Compiler::new(); - let blocks = registry_compiler.compile_str(®istry_file)?; + let sections = registry_compiler.compile_str(®istry_file)?; let mut registry_core = Core::new(); - registry_core.load_blocks(blocks); + registry_core.load_sections(sections); // Convert the machine listing into a hash map let registry_table = registry_core.get_table("mech/registry")?; let registry_table_brrw = registry_table.borrow(); for row in 0..registry_table_brrw.rows { let row_index = TableIndex::Index(row+1); - let name = registry_table_brrw.get_by_index(row_index, TableIndex::Alias(*NAME))?.as_string().unwrap().to_string(); - let version = registry_table_brrw.get_by_index(row_index, TableIndex::Alias(*VERSION))?.as_string().unwrap().to_string(); - let url = registry_table_brrw.get_by_index(row_index, TableIndex::Alias(*URL))?.as_string().unwrap().to_string(); + let name = registry_table_brrw.get_by_index(row_index.clone(), TableIndex::Alias(*NAME))?.as_string().unwrap().to_string(); + let version = registry_table_brrw.get_by_index(row_index.clone(), TableIndex::Alias(*VERSION))?.as_string().unwrap().to_string(); + let url = registry_table_brrw.get_by_index(row_index.clone(), TableIndex::Alias(*URL))?.as_string().unwrap().to_string(); self.machine_repository.insert(name, (version, url)); } } @@ -274,28 +280,33 @@ impl Program { match File::open(format!("machines/{}",machine_name)) { Ok(_) => { match &outgoing { - Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".bright_cyan(), m, ver)));} + Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".truecolor(153,221,85), m, ver)));} None => (), } let message = format!("Can't load library {:?}", machine_name); - unsafe{Library::new(format!("machines/{}",machine_name)).expect(&message)} + unsafe{Some(Library::new(format!("machines/{}",machine_name)).expect(&message))} } - _ => download_machine(&machine_name, m, path, ver, outgoing.clone()).unwrap() + _ => Some(download_machine(&machine_name, m, path, ver, outgoing.clone()).unwrap()) } }); // Replace slashes with underscores and then add a null terminator let mut s = format!("{}\0", fun_name.replace("-","__").replace("/","_")); let error_msg = format!("Symbol {} not found",s); let mut registrar = MechFunctions::new(); - unsafe{ - match library.get::<*mut MechFunctionDeclaration>(s.as_bytes()) { - Ok(good) => { - let declaration = good.read(); - (declaration.register)(&mut registrar); - } - Err(_) => { - println!("Couldn't find the specified machine: {}", fun_name); + unsafe { + match library { + Some(lib) => { + match lib.get::<*mut MechFunctionDeclaration>(s.as_bytes()) { + Ok(good) => { + let declaration = good.read(); + (declaration.register)(&mut registrar); + } + Err(_) => { + println!("Couldn't find the specified machine: {}", fun_name); + } + } } + None => (), } } self.mech.functions.borrow_mut().extend(registrar.mech_functions); @@ -324,7 +335,7 @@ impl Program { let mut machine_init_code = vec![]; for needed_table_id in needed_tables.iter() { let dictionary = self.mech.dictionary.borrow(); - let needed_table_name = dictionary.get(&needed_table_id.unwrap()).unwrap().to_string(); + let needed_table_name = dictionary.get(needed_table_id.unwrap()).unwrap().to_string(); let m: Vec<_> = needed_table_name.split('/').collect(); let needed_machine_id = hash_str(&m[0]); match self.loaded_machines.contains(&needed_machine_id) { @@ -342,13 +353,18 @@ impl Program { match File::open(format!("machines/{}",machine_name)) { Ok(_) => { match &outgoing { - Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".bright_cyan(), m[0], ver)));} + Some(sender) => {sender.send(ClientMessage::String(format!("{} {} v{}", "[Loading]".truecolor(153,221,85), m[0], ver)));} None => (), } let message = format!("Can't load library {:?}", machine_name); - unsafe{Library::new(format!("machines/{}",machine_name)).expect(&message)} + unsafe{Some(Library::new(format!("machines/{}",machine_name)).expect(&message))} + } + _ => { + match download_machine(&machine_name, m[0], path, ver, outgoing.clone()) { + Ok(library) => Some(library), + Err(err) => None, + } } - _ => download_machine(&machine_name, m[0], path, ver, outgoing.clone()).unwrap() } }); // Replace slashes with underscores and then add a null terminator @@ -356,15 +372,20 @@ impl Program { let error_msg = format!("Symbol {} not found",s); let mut registrar = Machines::new(); unsafe{ - match library.get::<*mut MachineDeclaration>(s.as_bytes()) { - Ok(good) => { - let declaration = good.read(); - let init_code = (declaration.register)(&mut registrar, self.outgoing.clone()); - machine_init_code.push(init_code); - } - Err(_) => { - println!("Couldn't find the specified machine: {}", needed_table_name); + match library { + Some(lib) => { + match lib.get::<*mut MachineDeclaration>(s.as_bytes()) { + Ok(good) => { + let declaration = good.read(); + let init_code = (declaration.register)(&mut registrar, self.outgoing.clone()); + machine_init_code.push(init_code); + } + Err(_) => { + println!("Couldn't find the specified machine: {}", needed_table_name); + } + } } + None => (), } } self.machines.extend(registrar.machines); @@ -377,13 +398,20 @@ impl Program { } // Load init code and trigger machines + let mut already_triggered = HashSet::new(); for mic in &machine_init_code { - let new_block_ids = self.compile_program(mic.to_string())?; + let result = self.compile_program(mic.to_string())?; self.mech.schedule_blocks(); - for block_id in new_block_ids { - let output = self.mech.get_output_by_block_id(block_id)?; - for register in output.iter() { - self.trigger_machine(register); + for (new_block_ids,_,block_error) in result { + for block_id in new_block_ids { + let block = self.mech.blocks.get(&block_id); + let output = self.mech.get_output_by_block_id(block_id)?; + for register in output.iter() { + if !already_triggered.contains(register) { + self.trigger_machine(register); + } + already_triggered.insert(register.clone()); + } } } } @@ -431,4 +459,19 @@ impl Program { self.mech.clear(); }*/ +} + +impl fmt::Debug for Program { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut box_drawing = BoxPrinter::new(); + box_drawing.add_title("🤖","Program"); + box_drawing.add_title(" ","cores"); + box_drawing.add_line(format!(" 1. (b {:?}, t {:?})", self.mech.blocks.len() , self.mech.database.borrow().tables.len())); + for (ix, core) in self.cores.iter() { + box_drawing.add_line(format!(" {:?}. (b {:?}, t {:?})", ix, core.blocks.len() , core.database.borrow().tables.len() )); + } + write!(f,"{:?}",box_drawing)?; + Ok(()) + } } \ No newline at end of file diff --git a/src/runloop.rs b/src/runloop.rs index b31ed76..de4d9d5 100644 --- a/src/runloop.rs +++ b/src/runloop.rs @@ -23,6 +23,9 @@ use std::time::Instant; use std::sync::Mutex; extern crate miniz_oxide; +extern crate bincode; +use std::io::{Write, BufReader, BufWriter, stdout}; +use std::fs::{OpenOptions, File, canonicalize, create_dir}; use miniz_oxide::inflate::decompress_to_vec; use miniz_oxide::deflate::compress_to_vec; @@ -56,14 +59,14 @@ pub enum ClientMessage { Stop, Pause, Resume, - Clear, + Reset, Exit(i32), Time(usize), NewBlocks(usize), Value(Value), Transaction(Transaction), String(String), - Error(MechErrorKind), + Error(MechError), Timing(f64), //Block(Block), StepDone, @@ -121,6 +124,7 @@ impl RunLoop { pub struct ProgramRunner { pub name: String, pub socket: Option>, + pub registry: String, //pub persistence_channel: Option>, } @@ -148,6 +152,7 @@ impl ProgramRunner { ProgramRunner { name: name.to_owned(), socket, + registry: "https://gitlab.com/mech-lang/machines/mech/-/raw/v0.1-beta/src/registry.mec".to_string(), //program, // TODO Use the persistence file specified by the user //persistence_channel: Some(persister.get_channel()), @@ -195,7 +200,7 @@ impl ProgramRunner { // Start a channel receiving thread let thread = thread::Builder::new().name(name.clone()).spawn(move || { - let mut program = Program::new("new program", 100, 1000, outgoing.clone(), program_incoming); + let mut program = Program::new("new program", 100, 1000, outgoing.clone(), program_incoming, self.registry); let program_channel_udpsocket = program.outgoing.clone(); let program_channel_udpsocket = program.outgoing.clone(); @@ -231,6 +236,7 @@ impl ProgramRunner { println!("Got a pong from: {:?}", src); } Ok(SocketMessage::Transaction(txn)) => { + program_channel_udpsocket.send(RunLoopMessage::String((format!("Received Txn: {:?}", txn),None))); program_channel_udpsocket.send(RunLoopMessage::Transaction(txn)); } Ok(x) => println!("Unhandled Message {:?}", x), @@ -247,9 +253,19 @@ impl ProgramRunner { None => (), } - let resolved_errors: Vec = program.download_dependencies(Some(client_outgoing.clone())).unwrap(); - program.mech.resolve_errors(&resolved_errors); - program.mech.schedule_blocks(); + match program.download_dependencies(Some(client_outgoing.clone())) { + Ok(resolved_errors) => { + let (_,_,nbo) = program.mech.resolve_errors(&resolved_errors); + program.mech.schedule_blocks(); + for output in nbo { + program.mech.step(&output); + } + } + Err(err) => { + client_outgoing.send(ClientMessage::Error(err.clone())); + } + } + // Step cores /*program.mech.step(); for core in program.cores.values_mut() { @@ -264,7 +280,7 @@ impl ProgramRunner { match (program.incoming.recv(), paused) { (Ok(RunLoopMessage::Transaction(txn)), false) => { // Process the transaction and calculate how long it took. - let start_ns = time::precise_time_ns(); + let now = Instant::now(); match program.mech.process_transaction(&txn) { Ok((new_block_ids,changed_registers)) => { for trigger_register in &changed_registers { @@ -286,7 +302,7 @@ impl ProgramRunner { // blocks that it potentially updated. We already have that list. If this register // has been triggered for the first time, then we need to get the list of // output blocks - match program.trigger_to_listener.entry(*trigger_register) { + match program.trigger_to_listener.entry(trigger_register.clone()) { // Already triggered in the past Entry::Occupied(mut o) => { // Here is the output that the triggered register will cause to update @@ -294,8 +310,8 @@ impl ProgramRunner { Some(output) => { // Is any of this being listened for? for (register,remote_cores) in &program.listeners { - if output.contains(®ister) { - o.insert((*register,remote_cores.clone())); + if output.contains(register) { + o.insert((register.clone(),remote_cores.clone())); break; } } @@ -313,7 +329,7 @@ impl ProgramRunner { let compressed_message = compress_to_vec(&message,6); // Send the transaction to the remote core for remote_core_id in listeners { - match (&self.socket,program.remote_cores.get_mut(&remote_core_id)) { + match (&self.socket,program.remote_cores.get_mut(remote_core_id)) { (Some(ref socket),Some(MechSocket::UdpSocket(remote_core_address))) => { let len = socket.send_to(&compressed_message, remote_core_address.clone()).unwrap(); } @@ -322,7 +338,7 @@ impl ProgramRunner { Ok(()) => (), Err(x) => { client_outgoing.send(ClientMessage::String(format!("Remote core disconnected: {}", humanize(&remote_core_id)))); - program.remote_cores.remove(&remote_core_id); + program.remote_cores.remove(remote_core_id); for (core_id, core_address) in &program.remote_cores { match core_address { MechSocket::UdpSocket(core_address) => { @@ -343,8 +359,8 @@ impl ProgramRunner { } } } - Err(MechError{id,kind}) => { - //client_outgoing.send(ClientMessage::Error(kind.clone())); + Err(err) => { + client_outgoing.send(ClientMessage::Error(err)); } } } @@ -355,8 +371,8 @@ impl ProgramRunner { Some(output) => { // Is any of this being listened for? for (register,remote_cores) in &program.listeners { - if output.contains(®ister) { - v.insert((*register,remote_cores.clone())); + if output.contains(register) { + v.insert((register.clone(),remote_cores.clone())); break; } } @@ -367,21 +383,22 @@ impl ProgramRunner { } } } - Err(MechError{id,kind}) => { - //client_outgoing.send(ClientMessage::Error(kind.clone())); + Err(err) => { + client_outgoing.send(ClientMessage::Error(err)); } }; - let end_ns = time::precise_time_ns(); - let time = (end_ns - start_ns) as f64; - client_outgoing.send(ClientMessage::Timing(1.0 / (time / 1_000_000_000.0))); + let elapsed_time = now.elapsed(); + let cycle_duration = elapsed_time.as_nanos() as f64; + client_outgoing.send(ClientMessage::Timing(1.0 / (cycle_duration / 1_000_000_000.0))); client_outgoing.send(ClientMessage::StepDone); }, (Ok(RunLoopMessage::Listening((core_id, register))), _) => { - let (table_id,row,col) = register; - match program.mech.output.contains(®ister) { + let (table_id,row,col) = ®ister; + let name = program.mech.get_name(*table_id.unwrap()).unwrap(); + match program.mech.output.contains(®ister.clone()) { // We produce a table for which they're listening true => { - client_outgoing.send(ClientMessage::String(format!("Sending {:?} to {}", table_id, humanize(&core_id)))); + client_outgoing.send(ClientMessage::String(format!("Sending #{} to {}", name, humanize(&core_id)))); // Mark down that this register has a listener for future updates let mut listeners = program.listeners.entry(register.clone()).or_insert(HashSet::new()); listeners.insert(core_id); @@ -473,7 +490,7 @@ impl ProgramRunner { Some(_) => { for register in &program.mech.needed_registers() { //println!("I'm listening for {:?}", register); - let message = bincode::serialize(&SocketMessage::Listening(*register)).unwrap(); + let message = bincode::serialize(&SocketMessage::Listening(register.clone())).unwrap(); let compressed_message = compress_to_vec(&message,6); let len = socket.send_to(&compressed_message, remote_core_address.clone()).unwrap(); } @@ -490,7 +507,7 @@ impl ProgramRunner { let (mut ws_incoming, mut ws_outgoing) = ws_stream.split().unwrap(); // Tell the remote websocket what this core is listening for for needed_register in &program.mech.needed_registers() { - let message = bincode::serialize(&SocketMessage::Listening(*needed_register)).unwrap(); + let message = bincode::serialize(&SocketMessage::Listening(needed_register.clone())).unwrap(); let compressed_message = compress_to_vec(&message,6); ws_outgoing.send_message(&OwnedMessage::Binary(compressed_message)).unwrap(); } @@ -536,82 +553,176 @@ impl ProgramRunner { } (Ok(RunLoopMessage::Exit(exit_code)), _) => { client_outgoing.send(ClientMessage::Exit(exit_code)); - } - (Ok(RunLoopMessage::Code(code)), _) => { + } + (Ok(RunLoopMessage::DumpCore(core_ix)), _) => { + let result = match core_ix { + 0 => { + let core = &program.mech; + let mut minicores = vec![]; + for (_,core) in &program.cores { + let minicore = MiniCore::minify_core(&core); + minicores.push(minicore); + } + bincode::serialize(&minicores).unwrap() + } + 1 => { + let core = &program.mech; + let minicore = MiniCore::minify_core(&core); + let minicores = MechCode::MiniCores(vec![minicore]); + bincode::serialize(&minicores).unwrap() + } + _ => { + let core = program.cores.get_mut(&core_ix).unwrap(); + let minicore = MiniCore::minify_core(&core); + let minicores = MechCode::MiniCores(vec![minicore]); + bincode::serialize(&minicores).unwrap() + } + }; + let output_name = format!("core-{}.blx",core_ix); + let file = OpenOptions::new().write(true).create(true).open(&output_name).unwrap(); + let mut writer = BufWriter::new(file); + if let Err(e) = writer.write_all(&result) { + panic!("{} Failed to write core(s)! {:?}", "[Error]".truecolor(170,51,85), e); + std::process::exit(1); + } + writer.flush().unwrap(); + client_outgoing.send(ClientMessage::String(format!("Wrote {:?}", output_name))); + client_outgoing.send(ClientMessage::Done); + } + (Ok(RunLoopMessage::NewCore), _) => { + let new_core = Core::new(); + let new_core_ix = program.cores.len() as u64 + 2; + program.cores.insert(new_core_ix, new_core); + client_outgoing.send(ClientMessage::Done); + } + (Ok(RunLoopMessage::Code((core_ix,code))), _) => { // Load the program - let blocks = match code { + let sections: Vec> = match code { + MechCode::MiniCores(cores) => { + for mc in cores { + let core = MiniCore::maximize_core(&mc); + let ix = program.cores.len() + 2; + program.cores.insert(ix as u64,core); + } + continue 'runloop; + } MechCode::String(code) => { let mut compiler = Compiler::new(); match compiler.compile_str(&code) { - Ok(blocks) => blocks, - Err(x) => { + Ok(sections) => sections, + Err(err) => { + client_outgoing.send(ClientMessage::Error(err)); client_outgoing.send(ClientMessage::StepDone); continue 'runloop; } } }, - MechCode::MiniBlocks(miniblocks) => { - let mut blocks: Vec = Vec::new(); - miniblocks.iter().map(|b| MiniBlock::maximize_block(&b)).collect() + MechCode::MiniBlocks(mb_sections) => { + let mut sections: Vec> = vec![]; + for section in mb_sections { + let section: Vec = section.iter().map(|mb| SectionElement::Block(MiniBlock::maximize_block(mb))).collect(); + sections.push(section); + } + sections } }; - let (mut new_block_ids, new_block_errors) = program.mech.load_blocks(blocks); + { - if new_block_errors.len() > 0 { - let resolved_errors: Vec = program.download_dependencies(Some(client_outgoing.clone())).unwrap(); - program.mech.resolve_errors(&resolved_errors); - program.mech.schedule_blocks(); - } + let result = { + let mut core: &mut Core = match core_ix { + 1 => &mut program.mech, + _ => program.cores.get_mut(&core_ix).unwrap(), + }; + core.load_sections(sections) + }; - if let Some(last_block_id) = new_block_ids.last() { - let block = program.mech.blocks.get(last_block_id).unwrap().borrow(); - let out_id = match block.transformations.last() { - Some(Transformation::Function{name,arguments,out}) => { - let (out_id,_,_) = out; - *out_id - } - Some(Transformation::TableDefine{table_id,indices,out}) => { - *out - } - Some(Transformation::Set{src_id, src_row, src_col, dest_id, dest_row, dest_col}) => { - *dest_id - } - Some(Transformation::TableAlias{table_id, alias}) => { - *table_id - } - Some(Transformation::Whenever{table_id, ..}) => { - *table_id - } - _ => { - TableId::Local(0) + for (new_block_ids,_,new_block_errors) in result { + if new_block_errors.len() > 0 { + match program.download_dependencies(Some(client_outgoing.clone())) { + Ok(resolved_errors) => { + let mut core: &mut Core = match core_ix { + 1 => &mut program.mech, + _ => program.cores.get_mut(&core_ix).unwrap(), + }; + let (_,_,nbo) = core.resolve_errors(&resolved_errors); + core.schedule_blocks(); + for output in nbo { + core.step(&output); + } + } + Err(err) => { + client_outgoing.send(ClientMessage::Error(err.clone())); + } + } + } + + if let Some(last_block_id) = new_block_ids.last() { + let mut core: &mut Core = match core_ix { + 1 => &mut program.mech, + _ => program.cores.get_mut(&core_ix).unwrap(), + }; + let block = core.blocks.get(last_block_id).unwrap().borrow(); + let out_id = match block.transformations.last() { + Some(Transformation::Function{name,arguments,out}) => { + let (out_id,_,_) = out; + *out_id + } + Some(Transformation::TableDefine{table_id,indices,out}) => { + *out + } + Some(Transformation::Set{src_id, src_row, src_col, dest_id, dest_row, dest_col}) => { + *dest_id + } + Some(Transformation::TableAlias{table_id, alias}) => { + *table_id + } + Some(Transformation::Whenever{table_id, ..}) => { + *table_id + } + _ => { + TableId::Local(0) + } + }; + if let Ok(out_table) = block.get_table(&out_id) { + client_outgoing.send(ClientMessage::String(format!("{:?}", out_table.borrow()))); + } } - }; - if let Ok(out_table) = block.get_table(&out_id) { - client_outgoing.send(ClientMessage::String(format!("{:?}", out_table.borrow()))); } - } - // React to errors - for (error,_) in program.mech.errors.iter() { - client_outgoing.send(ClientMessage::Error(error.clone())); + // React to errors + let mut core: &mut Core = match core_ix { + 1 => &mut program.mech, + _ => program.cores.get_mut(&core_ix).unwrap(), + }; + for (error,_) in core.full_errors.iter() { + client_outgoing.send(ClientMessage::Error(error.clone())); + } } client_outgoing.send(ClientMessage::StepDone); } - (Ok(RunLoopMessage::Clear), _) => { - /*program.clear(); - client_outgoing.send(ClientMessage::Clear);*/ + (Ok(RunLoopMessage::Reset(core_ix)), _) => { + let new_core = Core::new(); + match core_ix { + 1 => {program.mech = new_core;} + _ => {program.cores.insert(core_ix,new_core);}, + }; + client_outgoing.send(ClientMessage::Reset); }, (Ok(RunLoopMessage::PrintCore(core_id)), _) => { match core_id { - None => client_outgoing.send(ClientMessage::String(format!("There are {:?} cores running.", program.cores.len() + 1))), - Some(0) => client_outgoing.send(ClientMessage::String(format!("{:?}", program.mech))), - Some(core_id) => client_outgoing.send(ClientMessage::String(format!("{:?}", program.cores.get(&core_id)))), - }; + None => {client_outgoing.send(ClientMessage::String(format!("There are {:?} cores running.", program.cores.len() + 1)));} + Some(0) => {client_outgoing.send(ClientMessage::String("Core indices start a 1.".to_string()));} + Some(1) => {client_outgoing.send(ClientMessage::String(format!("{:?}", program.mech)));} + Some(core_id) => { + if core_id < program.cores.len() as u64 + 1 { + client_outgoing.send(ClientMessage::String(format!("{:?}", program.cores.get(&core_id).unwrap()))); + } + } + } }, - (Ok(RunLoopMessage::PrintDebug), _) => { - client_outgoing.send(ClientMessage::String(format!("{:?}",program.mech.blocks))); - client_outgoing.send(ClientMessage::String(format!("{:?}",program.mech))); + (Ok(RunLoopMessage::PrintInfo), _) => { + client_outgoing.send(ClientMessage::String(format!("{:?}", program))); }, (Ok(RunLoopMessage::PrintTable(table_id)), _) => { let result = match program.mech.get_table_by_id(table_id) { @@ -620,10 +731,6 @@ impl ProgramRunner { }; client_outgoing.send(ClientMessage::String(result)); }, - (Ok(RunLoopMessage::PrintRuntime), _) => { - //println!("{:?}", program.mech.runtime); - //client_outgoing.send(ClientMessage::String(format!("{:?}",program.mech.runtime))); - }, (Ok(RunLoopMessage::Blocks(miniblocks)), _) => { /*let mut blocks: Vec = Vec::new(); for miniblock in miniblocks { @@ -646,10 +753,10 @@ impl ProgramRunner { Ok(table) => { match table.borrow().get(&row,&column) { Ok(v) => ClientMessage::Value(v.clone()), - Err(error) => ClientMessage::Error(error.kind.clone()), + Err(error) => ClientMessage::Error(error.clone()), } } - Err(error) => ClientMessage::Error(error.kind.clone()), + Err(error) => ClientMessage::Error(error.clone()), }; client_outgoing.send(msg); }, @@ -676,7 +783,7 @@ impl ProgramRunner { (Err(_), _) => { break 'runloop }, - x => println!("qq{:?}", x), + x => println!("qq {:?}", x), } client_outgoing.send(ClientMessage::Done); }