From 02e46fe0f4b9f73f996ecf4bc8b708c9131f7957 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Tue, 6 Feb 2024 15:43:50 +0100 Subject: [PATCH] Add hovering! --- multiply_add.sus | 10 +- src/ast.rs | 2 +- src/dev_aid/lsp.rs | 223 ++++++++++++++++++++++++-------------- src/instantiation/mod.rs | 9 ++ src/linker.rs | 26 +++-- src/main.rs | 11 +- src/tokenizer.rs | 19 ++++ valid_syntax.sus | 228 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 434 insertions(+), 94 deletions(-) create mode 100644 valid_syntax.sus diff --git a/multiply_add.sus b/multiply_add.sus index a498b1c..13c660d 100644 --- a/multiply_add.sus +++ b/multiply_add.sus @@ -436,7 +436,15 @@ module undeteriminable_input_latency : int a, int b -> int x, int y { y = t; } -module manually_specified_input_latency : int a'0, int b'1 -> int x, int y { +module determinable_input_latency : int a, int b -> int x, int y { + reg int a_d = a; + reg int t = a_d + b; + reg reg int a_ddd = a; + x = t + a_ddd; + y = t; +} + +module specified_input_latency : int a'0, int b'1 -> int x, int y { reg int a_d = a; reg int t = a_d + b; reg reg reg int a_ddd = a; diff --git a/src/ast.rs b/src/ast.rs index b582d0b..acba4f3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -49,7 +49,7 @@ impl Span { Span(0, tokens.token_types.len()) } pub fn contains_token(&self, token_idx : usize) -> bool { - self.0 >= token_idx && self.1 <= token_idx + token_idx >= self.0 && token_idx <= self.1 } // Not really a useful quantity. Should only be used comparatively, find which is the nested-most span pub fn size(&self) -> usize { diff --git a/src/dev_aid/lsp.rs b/src/dev_aid/lsp.rs index 692f857..12f5191 100644 --- a/src/dev_aid/lsp.rs +++ b/src/dev_aid/lsp.rs @@ -1,12 +1,12 @@ use std::{error::Error, net::SocketAddr}; -use lsp_types::{*, request::Request, notification::*}; +use lsp_types::{notification::*, request::Request, *}; -use lsp_server::{Response, Message, Connection}; +use lsp_server::{Connection, Message, Response}; use lsp_types::notification::Notification; -use crate::{arena_alloc::ArenaVector, ast::{IdentifierType, Span}, dev_aid::syntax_highlighting::create_token_ide_info, errors::{ErrorCollector, CompileError, ErrorLevel}, linker::{FileUUIDMarker, Linker, FileUUID, FileData}, parser::perform_full_semantic_parse, tokenizer::{CharLine, TokenizeResult}}; +use crate::{arena_alloc::ArenaVector, ast::{IdentifierType, Span}, dev_aid::syntax_highlighting::create_token_ide_info, errors::{CompileError, ErrorCollector, ErrorLevel}, flattening::{WireInstance, WireSource}, linker::{FileData, FileUUID, FileUUIDMarker, Linker, LocationInfo}, parser::perform_full_semantic_parse, tokenizer::{CharLine, TokenizeResult}}; use super::syntax_highlighting::{IDETokenType, IDEIdentifierType, IDEToken}; @@ -24,7 +24,7 @@ impl LoadedFileCache { .find(|(_uuid, uri_found)| **uri_found == *uri) .map(|(uuid, _uri_found)| uuid) } - fn update_text(&mut self, uri : Url, new_file_text : String) { + fn update_text(&mut self, uri : Url, new_file_text : String) -> FileUUID { let found_opt = self.find_uri(&uri); let found_opt_was_none = found_opt.is_none(); let file_uuid : FileUUID = found_opt.unwrap_or_else(|| self.linker.reserve_file()); @@ -37,6 +37,8 @@ impl LoadedFileCache { self.linker.relink(file_uuid, full_parse); } self.linker.recompile_all(); + + file_uuid } fn ensure_contains_file(&mut self, uri : &Url) -> FileUUID { if let Some(found) = self.find_uri(uri) { @@ -53,11 +55,8 @@ impl LoadedFileCache { } } -pub fn lsp_main(port : u16) -> Result<(), Box> { - // Note that we must have our logging only write out to stderr. - //println!("starting generic LSP server"); - - println!("starting generic LSP server"); +pub fn lsp_main(port : u16, debug : bool) -> Result<(), Box> { + println!("starting LSP server"); // Create the transport. Includes the stdio (stdin and stdout) versions but this could // also be implemented to use sockets or HTTP. @@ -76,6 +75,7 @@ pub fn lsp_main(port : u16) -> Result<(), Box> { } } )), + hover_provider : Some(HoverProviderCapability::Simple(true)), semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions(SemanticTokensOptions{ work_done_progress_options: WorkDoneProgressOptions { work_done_progress: Some(false) @@ -116,7 +116,7 @@ pub fn lsp_main(port : u16) -> Result<(), Box> { }) .unwrap(); let initialization_params = connection.initialize(server_capabilities)?; - main_loop(connection, initialization_params)?; + main_loop(connection, initialization_params, debug)?; io_threads.join()?; // Shut down gracefully. @@ -156,53 +156,52 @@ fn get_modifiers_for_token(tok : &IDEToken) -> u32 { } } -struct SemanticTokensDeltaAccumulator { - prev : Position, - semantic_tokens : Vec +fn from_position(pos : lsp_types::Position) -> CharLine { + CharLine{line : pos.line as usize, character : pos.character as usize} +} +fn from_position_range(range : lsp_types::Range) -> std::ops::Range { + std::ops::Range{start : from_position(range.start), end : from_position(range.end)} +} +fn to_position(char_line : CharLine) -> lsp_types::Position { + lsp_types::Position{line : char_line.line as u32, character : char_line.character as u32} +} +fn to_position_range(range : std::ops::Range) -> lsp_types::Range { + lsp_types::Range{start : to_position(range.start), end : to_position(range.end)} } -impl SemanticTokensDeltaAccumulator { - fn push(&mut self, position : Position, length : u32, typ : u32, mod_bits : u32) { - let delta_line = position.line - self.prev.line; +fn do_syntax_highlight(file_data : &FileData, linker : &Linker) -> Vec { + let ide_tokens = create_token_ide_info(&file_data, linker); + + let mut cursor = Position {line : 0, character : 0}; + let mut semantic_tokens = Vec::with_capacity(file_data.tokens.len()); + + for (tok_idx, ide_tok) in ide_tokens.iter().enumerate() { + let typ = get_semantic_token_type_from_ide_token(ide_tok); + let mod_bits = get_modifiers_for_token(ide_tok); + + let tok_range = file_data.tokens.get_token_linechar_range(tok_idx); + let start_pos = to_position(tok_range.start); + let end_pos = to_position(tok_range.end); + + let delta_line = start_pos.line - cursor.line; if delta_line != 0 { - self.prev.character = 0; + cursor.character = 0; } - let delta_col = position.character - self.prev.character; - self.prev = position; + let delta_col = start_pos.character - cursor.character; + cursor = start_pos; - self.semantic_tokens.push(SemanticToken{ + semantic_tokens.push(SemanticToken{ delta_line: delta_line, delta_start: delta_col, - length: length, + length: end_pos.character - start_pos.character, token_type: typ, token_modifiers_bitset: mod_bits, }); } -} - -fn do_syntax_highlight(file_data : &FileData, linker : &Linker) -> SemanticTokensResult { - let ide_tokens = create_token_ide_info(&file_data, linker); - let mut semantic_tokens_acc = SemanticTokensDeltaAccumulator{prev : Position {line : 0, character : 0}, semantic_tokens : Vec::new()}; - semantic_tokens_acc.semantic_tokens.reserve(file_data.tokens.len()); - - for (tok_idx, ide_tok) in ide_tokens.iter().enumerate() { - let typ = get_semantic_token_type_from_ide_token(ide_tok); - let mod_bits = get_modifiers_for_token(ide_tok); - - - let tok_range = file_data.tokens.get_token_linechar_range(tok_idx); - let start_pos = Position{line : tok_range.start.line as u32, character : tok_range.start.character as u32}; - let end_pos = Position{line : tok_range.end.line as u32, character : tok_range.end.character as u32}; - semantic_tokens_acc.push(start_pos, end_pos.character - start_pos.character, typ, mod_bits) - } - - SemanticTokensResult::Tokens(lsp_types::SemanticTokens { - result_id: None, - data: semantic_tokens_acc.semantic_tokens - }) + semantic_tokens } use lsp_types::Diagnostic; @@ -255,10 +254,21 @@ fn send_errors_warnings(connection: &Connection, errors : ErrorCollector, token_ Ok(()) } -fn main_loop( - connection: Connection, - params: serde_json::Value, -) -> Result<(), Box> { +fn get_hover_info<'l>(file_cache : &'l LoadedFileCache, text_pos : &lsp_types::TextDocumentPositionParams) -> Option<(LocationInfo<'l>, Option)> { + let uuid = file_cache.find_uri(&text_pos.text_document.uri).unwrap(); + + let file_data = &file_cache.linker.files[uuid]; + + let token_idx = file_data.tokens.get_token_on_or_left_of(from_position(text_pos.position)); + + let (info, span) = file_cache.linker.get_info_about_source_location(token_idx, uuid)?; + //let span = Span::new_single_token(token_idx); + + let char_line_range = file_data.tokens.get_span_linechar_range(span); + Some((info, Some(to_position_range(char_line_range)))) +} + +fn main_loop(connection: Connection, params: serde_json::Value, debug : bool) -> Result<(), Box> { let mut file_cache = LoadedFileCache::new(Linker::new(), ArenaVector::new()); @@ -268,43 +278,107 @@ fn main_loop( println!("got msg: {msg:?}"); match msg { Message::Request(req) => { - match req.method.as_str() { + let response_value = match req.method.as_str() { request::Shutdown::METHOD => { println!("Shutdown request"); return Ok(()); } + request::HoverRequest::METHOD => { + let params : HoverParams = serde_json::from_value(req.params).expect("JSON Encoding Error while parsing params"); + println!("got hover request: {params:?}"); + + file_cache.ensure_contains_file(¶ms.text_document_position_params.text_document.uri); + serde_json::to_value(&if let Some((info, range)) = get_hover_info(&file_cache, ¶ms.text_document_position_params) { + let mut hover_list : Vec = Vec::new(); + if debug { + hover_list.push(MarkedString::String(format!("{info:?}"))) + } else { + match info { + LocationInfo::WireRef(md, decl_id) => { + let decl = md.flattened.instructions[decl_id].extract_wire_declaration(); + let typ_str = decl.typ.to_string(&file_cache.linker.types); + let name_str = &decl.name; + hover_list.push(MarkedString::String(format!("{typ_str} {name_str}"))); + + md.instantiations.for_each_instance(|inst| { + for (_id, wire) in &inst.wires { + if wire.original_wire != decl_id {continue} + let typ_str = wire.typ.to_string(&file_cache.linker.types); + let name_str = &wire.name; + let latency = wire.absolute_latency; + hover_list.push(MarkedString::String(format!("{typ_str} {name_str}'{latency}"))); + } + }); + } + LocationInfo::Temporary(md, id, wire) => { + let typ_str = wire.typ.to_string(&file_cache.linker.types); + + hover_list.push(MarkedString::String(format!("{typ_str}"))); + md.instantiations.for_each_instance(|inst| { + for (_id, wire) in &inst.wires { + if wire.original_wire != id {continue} + let typ_str = wire.typ.to_string(&file_cache.linker.types); + let name_str = &wire.name; + let latency = wire.absolute_latency; + hover_list.push(MarkedString::String(format!("{typ_str} {name_str}'{latency}"))); + } + }); + } + LocationInfo::Type(typ) => { + hover_list.push(MarkedString::String(typ.to_type().to_string(&file_cache.linker.types))); + } + LocationInfo::Global(global) => { + hover_list.push(MarkedString::String(file_cache.linker.get_full_name(global))); + } + }; + } + Hover{contents: HoverContents::Array(hover_list), range} + } else { + Hover{contents: HoverContents::Array(Vec::new()), range: None} + }) + } request::GotoDefinition::METHOD => { let params : GotoDefinitionParams = serde_json::from_value(req.params).expect("JSON Encoding Error while parsing params"); println!("got gotoDefinition request: {params:?}"); - let pos = ¶ms.text_document_position_params.position; - let text_document = ¶ms.text_document_position_params.text_document; - - let uuid = file_cache.ensure_contains_file(&text_document.uri); - - - let file_data = &file_cache.linker.files[uuid]; - - let result = Some(GotoDefinitionResponse::Array(Vec::new())); - let result = serde_json::to_value(&result).unwrap(); - let resp = Response { id: req.id, result: Some(result), error: None }; - connection.sender.send(Message::Response(resp))?; - }, + file_cache.ensure_contains_file(¶ms.text_document_position_params.text_document.uri); + serde_json::to_value(&if let Some((info, range)) = get_hover_info(&file_cache, ¶ms.text_document_position_params) { + + + GotoDefinitionResponse::Array(Vec::new()) + } else { + GotoDefinitionResponse::Array(Vec::new()) + }) + } request::SemanticTokensFullRequest::METHOD => { let params : SemanticTokensParams = serde_json::from_value(req.params).expect("JSON Encoding Error while parsing params"); - println!("got fullSemanticTokens request: {params:?}"); let uuid = file_cache.ensure_contains_file(¶ms.text_document.uri); let file_data = &file_cache.linker.files[uuid]; - let syntax_highlight = do_syntax_highlight(file_data, &file_cache.linker); + serde_json::to_value(&SemanticTokensResult::Tokens(lsp_types::SemanticTokens {result_id: None, data: do_syntax_highlight(file_data, &file_cache.linker)})) + } + // TODO ... + req => { + println!("Other request: {req:?}"); + continue; + } + }; - let result = serde_json::to_value(&syntax_highlight).unwrap(); - connection.sender.send(Message::Response(Response{ - id: req.id, result: Some(result), error: None - }))?; + let result = response_value.unwrap(); + let response = Response { id : req.id, result: Some(result), error: None }; + connection.sender.send(Message::Response(response))?; + } + Message::Response(resp) => { + println!("got response: {resp:?}"); + } + Message::Notification(not) => { + match not.method.as_str() { + notification::DidChangeTextDocument::METHOD => { + let params : DidChangeTextDocumentParams = serde_json::from_value(not.params).expect("JSON Encoding Error while parsing params"); + let uuid = file_cache.update_text(params.text_document.uri, params.content_changes.into_iter().next().unwrap().text); // println!("Flattening..."); file_cache.linker.recompile_all(); @@ -316,22 +390,7 @@ fn main_loop( // println!("Errors: {:?}", &errors); send_errors_warnings(&connection, errors, &file_data.tokens, &file_cache.uris)?; - }, - // TODO ... - req => { - println!("Other request: {req:?}"); } - } - } - Message::Response(resp) => { - println!("got response: {resp:?}"); - } - Message::Notification(not) => { - match not.method.as_str() { - notification::DidChangeTextDocument::METHOD => { - let params : DidChangeTextDocumentParams = serde_json::from_value(not.params).expect("JSON Encoding Error while parsing params"); - file_cache.update_text(params.text_document.uri, params.content_changes.into_iter().next().unwrap().text); - }, other => { println!("got notification: {other:?}"); } diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index 6d244a7..d38bdb6 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -680,4 +680,13 @@ impl InstantiationList { pub fn clear_instances(&mut self) { self.cache.borrow_mut().clear() } + + pub fn for_each_instance(&self, mut f : F) { + let borrow = self.cache.borrow(); + for v in borrow.iter() { + if let Some(vv) = &v.0 { + f(vv.as_ref()) + } + } + } } diff --git a/src/linker.rs b/src/linker.rs index 80de90a..d3f189b 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -1,6 +1,6 @@ use std::{collections::{HashMap, HashSet}, rc::Rc, cell::RefCell}; -use crate::{arena_alloc::{ArenaAllocator, UUID, UUIDMarker}, ast::{Module, LinkInfo, Span}, errors::{ErrorCollector, error_info}, flattening::{ConnectionWrite, FlatID, FlattenedModule, Instruction, WireInstance}, instantiation::InstantiatedModule, parser::{FullParseResult, TokenTreeNode}, tokenizer::TokenizeResult, typing::{WrittenType, Type}, util::{const_str_position, const_str_position_in_tuples}, value::Value}; +use crate::{arena_alloc::{ArenaAllocator, UUIDMarker, UUID}, ast::{LinkInfo, Module, Span}, errors::{error_info, ErrorCollector}, flattening::{ConnectionWrite, FlatID, FlattenedModule, Instruction, WireInstance, WireSource}, instantiation::InstantiatedModule, parser::{FullParseResult, TokenTreeNode}, tokenizer::TokenizeResult, typing::{Type, WrittenType}, util::{const_str_position, const_str_position_in_tuples}, value::Value}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ModuleUUIDMarker; @@ -219,6 +219,13 @@ impl Linker { } } } + pub fn get_full_name(&self, global : NameElem) -> String { + match global { + NameElem::Module(id) => self.modules[id].link_info.get_full_name(), + NameElem::Type(id) => self.types[id].get_full_name(), + NameElem::Constant(id) => self.constants[id].get_full_name(), + } + } fn get_linking_error_location(&self, global : NameElem) -> LinkingErrorLocation { match global { NameElem::Module(id) => { @@ -381,7 +388,8 @@ impl Linker { NameElem::Module(md_id) => { let md = &self.modules[md_id]; if md.link_info.span.contains_token(token_idx) { - for (_id, inst) in &md.flattened.instructions { + location_builder.update(md.link_info.name_span, LocationInfo::Global(NameElem::Module(md_id))); + for (id, inst) in &md.flattened.instructions { match inst { Instruction::SubModule(sm) => { location_builder.update(sm.module_name_span, LocationInfo::Global(NameElem::Module(sm.module_uuid))); @@ -392,10 +400,15 @@ impl Linker { } } Instruction::Wire(wire) => { - location_builder.update(wire.span, LocationInfo::Wire(md, wire)); + let loc_info = if let WireSource::WireRead(decl_id) = &wire.source { + LocationInfo::WireRef(md, *decl_id) + } else { + LocationInfo::Temporary(md, id, wire) + }; + location_builder.update(wire.span, loc_info); } Instruction::Write(write) => { - location_builder.update(Span::new_single_token(write.to.span.0), LocationInfo::WriteWire(md, &write.to)); + location_builder.update(Span::new_single_token(write.to.span.0), LocationInfo::WireRef(md, write.to.root)); } Instruction::IfStatement(_) | Instruction::ForStatement(_) => {} }; @@ -419,9 +432,10 @@ impl Linker { } } +#[derive(Clone, Copy, Debug)] pub enum LocationInfo<'linker> { - WriteWire(&'linker Module, &'linker ConnectionWrite), - Wire(&'linker Module, &'linker WireInstance), + WireRef(&'linker Module, FlatID), + Temporary(&'linker Module, FlatID, &'linker WireInstance), Type(&'linker WrittenType), Global(NameElem) } diff --git a/src/main.rs b/src/main.rs index 73a6815..47c6791 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,7 +80,7 @@ fn main() -> Result<(), Box> { let _executable_path = args.next(); let mut file_paths : Vec = Vec::new(); - let mut is_lsp = false; + let mut is_lsp = None; let mut codegen = None; let mut codegen_all = false; let mut test_sus_sitter = false; @@ -91,7 +91,10 @@ fn main() -> Result<(), Box> { while let Some(arg) = args.next() { match arg.as_str() { "--lsp" => { - is_lsp = true; + is_lsp = Some(false); + } + "--lsp-debug" => { + is_lsp = Some(true); } "--codegen" => { codegen = Some(args.next().expect("Expected a module name after --codegen")); @@ -112,8 +115,8 @@ fn main() -> Result<(), Box> { } #[cfg(feature = "lsp")] - if is_lsp { - return dev_aid::lsp::lsp_main(25000); + if let Some(debug) = is_lsp { + return dev_aid::lsp::lsp_main(25000, debug); } if file_paths.len() == 0 { // Quick debugging diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 4b366c1..b97b7eb 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -210,6 +210,16 @@ pub struct CharLine { pub line : usize, pub character : usize } +impl PartialOrd for CharLine { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for CharLine { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.line.cmp(&other.line).then(self.character.cmp(&other.character)) + } +} pub struct TokenizeResult { pub token_types : Vec, @@ -265,6 +275,15 @@ impl TokenizeResult { pub fn get_span_linechar_range(&self, span : Span) -> Range { self.token_boundaries_as_char_lines[span.0*2+1]..self.token_boundaries_as_char_lines[span.1*2+2] } + + pub fn get_token_on_or_left_of(&self, char_line : CharLine) -> usize { + match self.token_boundaries_as_char_lines.binary_search(&char_line) { + Ok(idx) | Err(idx) => { + assert!(idx >= 1); + return (idx - 1) / 2; + } + } + } } pub fn tokenize<'txt>(file_text : &'txt str, errors : &ErrorCollector) -> TokenizeResult { diff --git a/valid_syntax.sus b/valid_syntax.sus new file mode 100644 index 0000000..8b5e130 --- /dev/null +++ b/valid_syntax.sus @@ -0,0 +1,228 @@ + + + +module multiply_add : + int a, + int b, + int c + -> int total { + + reg int tmp = a * b; + total = tmp + c; +} + +module fibonnaci : -> int num { + state int current = 1; + state int current_prev = 0; + + num = current + current_prev; + current_prev = current; + current = num; +} + +//timeline (v, true -> /) .. (v, false -> v)* +module blur2 : + int data, + bool first + -> int blurred { + + state int prev; + + if !first { + blurred = data + prev; + } + prev = data; + + gen int a; + + gen bool b = true; + gen bool bb = false; + + if bb { + a = 5; + } else { + a = 3; + } +} + + +module Tree_Multiply : int[4] values -> int total { + int a = values[0] * values[1]; + int b = values[2] * values[3]; + total = a * b; +} + + + + +//timeline (X, false -> /)* .. (X, true -> T) +module Accumulator : int term, bool done -> int total { + state int tot; + initial tot = 0; + + int new_tot = tot + term; + if done { + total = new_tot; + tot = 0; // Must restore initial conditions + } else { + tot = new_tot; + } +} + + +//timeline (a, true -> /) | (a, false -> /) .. (a, false -> r)* .. (a, true -> r) +module blur : int a, bool done -> int result { + state bool working; + initial working = false; + state int prev; + + if working { + reg reg reg result = prev + a; // Add a pipeline stage for shits and giggles + } + prev = a; + working = !done; +} + + + +//timeline (X -> X) .. (/ -> X) .. (/ -> X) .. (/ -> X) +module Unpack4 : int[4] packed -> int out_stream { + state int st; + initial st = 0; + state int[3] stored_packed; + + if st == 0 { + out_stream = packed[0]; + stored_packed[0] = packed[1]; // Shorthand notation is possible here "stored_packed[0:2] = packed[1:3];" + stored_packed[1] = packed[2]; + stored_packed[2] = packed[3]; + st = 1; + } else if st == 1 { + out_stream = stored_packed[0]; + st = 2; + } else if st == 2 { + out_stream = stored_packed[1]; + st = 3; + } else if st == 3 { + out_stream = stored_packed[2]; + st = 0; // Must restore initial conditions + //finish; // packet is hereby finished. + } +} + +module generative : int i -> int o, int o2 { + gen int x = 5; + gen int[x] ys; + + //gen int[ys] zs; + + gen int[3] ps; + + gen int[x] a; + + a[2] = 5; + a[1] = 2; + a[0] = 10; + gen int[3] xx = a; + + gen bool test = true; + + if test { + + } + + o = a[i]; + o2 = a[a[0]]; +} + +module add_stuff_to_indices : int[10] values -> int[10] added_values { + for int i in 0..10 { + int sum = values[i] + i; + added_values[i] = sum; + } +} + + +//timeline (bs -> /, true) | (bs -> v, false) +module first_bit_idx_6 : bool[6] bits -> int first, bool all_zeros { + if bits[0] { + first = 0; + all_zeros = false; + } else if bits[1] { + first = 1; + all_zeros = false; + } else if bits[2] { + first = 2; + all_zeros = false; + } else if bits[3] { + first = 3; + all_zeros = false; + } else if bits[4] { + first = 4; + all_zeros = false; + } else if bits[5] { + first = 5; + all_zeros = false; + } else { + all_zeros = true; + } + + /*first int i in 0..6 where bits[i] { + first = i; + all_zeros = false; + } else { + all_zeros = true; + }*/ + +} + + +module disjoint_ports : int a, int b, int c -> int result { + reg result = a + b; + // don't touch c +} + +module undeteriminable_input_latency : int a, int b -> int x, int y { + reg int a_d = a; + reg int t = a_d + b; + reg reg reg int a_ddd = a; + x = t + a_ddd; + y = t; +} + +module determinable_input_latency : int a, int b -> int x, int y { + reg int a_d = a; + reg int t = a_d + b; + reg reg int a_ddd = a; + x = t + a_ddd; + y = t; +} + +module specified_input_latency : int a'0, int b'1 -> int x, int y { + reg int a_d = a; + reg int t = a_d + b; + reg reg reg int a_ddd = a; + x = t + a_ddd; + y = t; +} + +module bad_cycle : int a -> int r { + state int test; + initial test = 0; + + reg int new_test = test + a; + test = new_test; + + r = new_test; +} + +module good_cycle : int a -> int r { + state int test; + initial test = 0; + + int new_test = test + a; + test = new_test; + + r = new_test; +} +