From 74e59d30c79597be90199871b25f28f1631ac579 Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Thu, 18 Jul 2024 18:03:20 +0200 Subject: [PATCH] Add extern & __builtin__ --- README.md | 11 +- core.sus | 15 +-- src/codegen_fallback.rs | 200 +++++++++++++++++++---------- src/flattening/flatten.rs | 32 +++-- src/flattening/initialization.rs | 106 ++++++++------- src/instantiation/latency_count.rs | 2 +- src/linker/mod.rs | 8 ++ src/main.rs | 10 +- test.sus | 1 - tinyTestFile.sus | 14 +- tree-sitter-sus | 2 +- 11 files changed, 248 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index 6d316ea..36f22ba 100644 --- a/README.md +++ b/README.md @@ -150,11 +150,12 @@ In this example, we create a memory block with a read port and a write port. Thi - [x] Generative For Loops - [ ] Generative While Loops - [x] Generative Parameters +- [ ] Generative Default Arguments - [x] Type Parameters - [ ] Generative Asserts - [x] Multi-Interface Syntax -- [ ] Native Module integration syntax -- [ ] Intrinsic Modules +- [x] Native Module integration syntax +- [x] Intrinsic Modules - [x] Can Parse FIFO implementation - [ ] Clock Domain Crossings - [ ] Rhythm Syntax @@ -187,10 +188,10 @@ In this example, we create a memory block with a read port and a write port. Thi - [x] Indeterminable port latency - [x] Latency Counting uses latency specifiers - [x] Latency for output-only modules -- [x] Latency Counting is invariant across arbitrary algorithm starting nodes +- [x] Latency Counting is invariant across arbitrary algorithm starting nodes (not quite, some starting nodes may error. But those that don't are equivalent!) - [x] Integrate into Verilog generation -- [ ] Latency cuts -- [ ] Latency Offset +- [x] Latency cuts +- [x] Latency Offset - [ ] Latency Cuts & Latency Counting for "disjoint Input-Output blocks" - [ ] Split Latencies diff --git a/core.sus b/core.sus index d929ee2..437eac8 100644 --- a/core.sus +++ b/core.sus @@ -1,26 +1,21 @@ // Compiler Intrinsic -module LatencyOffset { +__builtin__ module LatencyOffset { input gen int OFFSET interface LatencyOffset : T in'0 -> T out'OFFSET - - // action start : (bool do_start) { - // } - // query pop : (bool do_pop), -> bool valid, T data {} - // trigger iter : -> bool valid } // Compiler Intrinsic -module CrossDomain { +__builtin__ module CrossDomain { interface in_domain : T in'0 domain out - interface out_domain : T out'0 + interface out_domain : -> T out'0 } -module IntToBits { +__builtin__ module IntToBits { interface IntToBits : int value'0 -> bool[32] bits'0 } -module BitsToInt { +__builtin__ module BitsToInt { interface IntToBits : bool[32] bits'0 -> int value'0 } diff --git a/src/codegen_fallback.rs b/src/codegen_fallback.rs index c4e3f2f..e5d39bc 100644 --- a/src/codegen_fallback.rs +++ b/src/codegen_fallback.rs @@ -1,8 +1,9 @@ use std::ops::Deref; +use crate::linker::IsExtern; use crate::prelude::*; -use crate::flattening::{DeclarationPortInfo, Instruction, Module}; +use crate::flattening::{DeclarationPortInfo, Instruction, Module, Port}; use crate::instantiation::{ InstantiatedModule, RealWire, RealWireDataSource, RealWirePathElem, CALCULATE_LATENCY_LATER, }; @@ -19,10 +20,21 @@ fn get_type_name_size(id: TypeUUID) -> u64 { } } +pub fn mangle(str: &str) -> String { + let mut result = String::with_capacity(str.len()); + for c in str.chars() { + if c.is_whitespace() || c == ':' { + continue; + } + result.push(if c.is_alphanumeric() { c } else { '_' }); + } + result +} + /// Creates the Verilog variable declaration for tbis variable. /// /// IE for `int[15] myVar` it creates `logic[31:0] myVar[14:0]` -fn typ_to_verilog_array(mut typ: &ConcreteType, var_name: &str) -> String { +fn typ_to_declaration(mut typ: &ConcreteType, var_name: &str) -> String { let mut array_string = String::new(); while let ConcreteType::Array(arr) = typ { let (content_typ, size) = arr.deref(); @@ -45,16 +57,6 @@ fn typ_to_verilog_array(mut typ: &ConcreteType, var_name: &str) -> String { } } -struct CodeGenerationContext<'g, 'out, Stream: std::fmt::Write> { - md: &'g Module, - instance: &'g InstantiatedModule, - program_text: &'out mut Stream, - - use_latency: bool, - - needed_untils : FlatAlloc -} - fn wire_name_with_latency(wire: &RealWire, absolute_latency: i64, use_latency: bool) -> String { assert!(wire.absolute_latency <= absolute_latency); @@ -69,6 +71,16 @@ fn wire_name_self_latency(wire: &RealWire, use_latency: bool) -> String { wire_name_with_latency(wire, wire.absolute_latency, use_latency) } +struct CodeGenerationContext<'g, 'out, Stream: std::fmt::Write> { + md: &'g Module, + instance: &'g InstantiatedModule, + program_text: &'out mut Stream, + + use_latency: bool, + + needed_untils : FlatAlloc +} + impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> { /// This is for making the resulting Verilog a little nicer to read fn can_inline(&self, wire: &RealWire) -> bool { @@ -119,46 +131,71 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> let from = wire_name_with_latency(w, i, self.use_latency); let to = wire_name_with_latency(w, i + 1, self.use_latency); - let var_decl = typ_to_verilog_array(&w.typ, &to); + let var_decl = typ_to_declaration(&w.typ, &to); writeln!( self.program_text, "/*latency*/ {var_decl}; always_ff @(posedge clk) begin {to} <= {from}; end" - )?; + ).unwrap(); } } Ok(()) } - fn write_verilog_code(&mut self) -> Result<(), std::fmt::Error> { + fn write_verilog_code(&mut self) { + match self.md.link_info.is_extern { + IsExtern::Normal => { + self.write_module_signature(false); + self.write_wire_declarations(); + self.write_submodules(); + self.write_multiplexers(); + self.write_endmodule(); + } + IsExtern::Extern => { + // Do nothing, it's provided externally + writeln!(self.program_text, "// Provided externally").unwrap(); + self.write_module_signature(true); + } + IsExtern::Builtin => { + self.write_module_signature(false); + self.write_builtins(); + self.write_endmodule(); + } + } + } + + fn write_module_signature(&mut self, commented_out : bool) { + let comment_text = if commented_out { "// " } else { "" }; // First output the interface of the module - writeln!(self.program_text, "module {}(", mangle(&self.instance.name))?; - write!(self.program_text, "\tinput clk")?; + write!(self.program_text, "{comment_text}module {}(\n{comment_text}\tinput clk", mangle(&self.instance.name)).unwrap(); for (_id, port) in self.instance.interface_ports.iter_valids() { let port_wire = &self.instance.wires[port.wire]; let input_or_output = if port.is_input { "input" } else { "output" }; let wire_doc = port_wire.source.get_sv_info_doc(); let wire_name = wire_name_self_latency(port_wire, self.use_latency); - let wire_decl = typ_to_verilog_array(&port_wire.typ, &wire_name); + let wire_decl = typ_to_declaration(&port_wire.typ, &wire_name); write!( self.program_text, - ",\n\t{wire_doc}{input_or_output} {wire_decl}" - )?; + ",\n{comment_text}\t{wire_doc}{input_or_output} {wire_decl}" + ).unwrap(); } - writeln!(self.program_text, "")?; - writeln!(self.program_text, ");\n")?; + write!(self.program_text, "\n{comment_text});\n\n").unwrap(); + + // Add latency registers for the interface declarations + // Should not appear in the program text for extern modules for (_id, port) in self.instance.interface_ports.iter_valids() { let port_wire = &self.instance.wires[port.wire]; - self.add_latency_registers(port.wire, port_wire)?; + self.add_latency_registers(port.wire, port_wire).unwrap(); } + } - // Then output all declarations, and the wires we can already assign + fn write_wire_declarations(&mut self) { for (wire_id, w) in &self.instance.wires { // For better readability of output Verilog if self.can_inline(w) { continue; } - + if let Instruction::Declaration(wire_decl) = &self.md.instructions[w.original_instruction] { @@ -168,16 +205,16 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> } } let wire_or_reg = w.source.get_sv_info_doc(); - + let wire_name = wire_name_self_latency(w, self.use_latency); - let wire_decl = typ_to_verilog_array(&w.typ, &wire_name); - write!(self.program_text, "{wire_or_reg}{wire_decl}")?; - + let wire_decl = typ_to_declaration(&w.typ, &wire_name); + write!(self.program_text, "{wire_or_reg}{wire_decl}").unwrap(); + match &w.source { RealWireDataSource::Select { root, path } => { let wire_name = self.wire_name(*root, w.absolute_latency); let path = self.wire_ref_path_to_string(&path, w.absolute_latency); - writeln!(self.program_text, " = {wire_name}{path};")?; + writeln!(self.program_text, " = {wire_name}{path};").unwrap(); } RealWireDataSource::UnaryOp { op, right } => { writeln!( @@ -185,7 +222,7 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> " = {}{};", op.op_text(), self.wire_name(*right, w.absolute_latency) - )?; + ).unwrap(); } RealWireDataSource::BinaryOp { op, left, right } => { writeln!( @@ -194,34 +231,35 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> self.wire_name(*left, w.absolute_latency), op.op_text(), self.wire_name(*right, w.absolute_latency) - )?; + ).unwrap(); } RealWireDataSource::Constant { value } => { - writeln!(self.program_text, " = {};", value.to_string())?; + writeln!(self.program_text, " = {};", value.to_string()).unwrap(); } RealWireDataSource::ReadOnly => { - writeln!(self.program_text, ";")?; + writeln!(self.program_text, ";").unwrap(); } RealWireDataSource::Multiplexer { is_state, sources: _, } => { - writeln!(self.program_text, ";")?; + writeln!(self.program_text, ";").unwrap(); if let Some(initial_value) = is_state { if initial_value.is_valid() { let initial_value_str = initial_value.to_string(); writeln!( self.program_text, "initial {wire_name} = {initial_value_str};" - )?; + ).unwrap(); } } } } - self.add_latency_registers(wire_id, w)?; + self.add_latency_registers(wire_id, w).unwrap(); } + } - // Output all submodules + fn write_submodules(&mut self) { for (_id, sm) in &self.instance.submodules { let sm_inst: &InstantiatedModule = sm .instance @@ -229,8 +267,8 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> .expect("Invalid submodules are impossible to remain by the time codegen happens"); let sm_instance_name = mangle(&sm_inst.name); let sm_name = &sm.name; - writeln!(self.program_text, "{sm_instance_name} {sm_name}(")?; - write!(self.program_text, "\t.clk(clk)")?; + writeln!(self.program_text, "{sm_instance_name} {sm_name}(").unwrap(); + write!(self.program_text, "\t.clk(clk)").unwrap(); for (port_id, iport) in sm_inst.interface_ports.iter_valids() { let port_name = wire_name_self_latency(&sm_inst.wires[iport.wire], self.use_latency); @@ -243,27 +281,28 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> // Ports that are defined on the submodule, but not used by impl String::new() }; - write!(self.program_text, ",\n\t.{port_name}({wire_name})")?; + write!(self.program_text, ",\n\t.{port_name}({wire_name})").unwrap(); } - writeln!(self.program_text, "\n);")?; + writeln!(self.program_text, "\n);").unwrap(); } + } - // For multiplexers, output + fn write_multiplexers(&mut self) { for (_id, w) in &self.instance.wires { match &w.source { RealWireDataSource::Multiplexer { is_state, sources } => { let output_name = wire_name_self_latency(w, self.use_latency); let arrow_str = if is_state.is_some() { - writeln!(self.program_text, "always_ff @(posedge clk) begin")?; + writeln!(self.program_text, "always_ff @(posedge clk) begin").unwrap(); "<=" } else { - writeln!(self.program_text, "always_comb begin")?; + writeln!(self.program_text, "always_comb begin").unwrap(); let invalid_val = w.typ.get_initial_val(); let invalid_val_text = invalid_val.to_string(); - writeln!(self.program_text, "\t{output_name} = {invalid_val_text}; // Combinatorial wires are not defined when not valid")?; + writeln!(self.program_text, "\t{output_name} = {invalid_val_text}; // Combinatorial wires are not defined when not valid").unwrap(); "=" }; - + for s in sources { let path = self.wire_ref_path_to_string(&s.to_path, w.absolute_latency); let from_name = self.wire_name(s.from.from, w.absolute_latency); @@ -272,12 +311,12 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> writeln!( self.program_text, "\tif({cond_name}) begin {output_name}{path} {arrow_str} {from_name}; end" - )?; + ).unwrap(); } else { - writeln!(self.program_text, "\t{output_name}{path} {arrow_str} {from_name};")?; + writeln!(self.program_text, "\t{output_name}{path} {arrow_str} {from_name};").unwrap(); } } - writeln!(self.program_text, "end")?; + writeln!(self.program_text, "end").unwrap(); } RealWireDataSource::ReadOnly => {} RealWireDataSource::Select { root: _, path: _ } => {} @@ -290,13 +329,55 @@ impl<'g, 'out, Stream: std::fmt::Write> CodeGenerationContext<'g, 'out, Stream> RealWireDataSource::Constant { value: _ } => {} } } + } + + /// TODO probably best to have some smarter system for this in the future. + fn write_builtins(&mut self) { + match self.md.link_info.name.as_str() { + "LatencyOffset" => { + let _in_port = self.md.unwrap_port(PortID::from_hidden_value(0), true, "in"); + let _out_port = self.md.unwrap_port(PortID::from_hidden_value(1), false, "out"); + self.program_text.write_str("\tassign out = in;\n").unwrap(); + } + "CrossDomain" => { + let _in_port = self.md.unwrap_port(PortID::from_hidden_value(0), true, "in"); + let _out_port = self.md.unwrap_port(PortID::from_hidden_value(1), false, "out"); + self.program_text.write_str("\tassign out = in;\n").unwrap(); + } + "IntToBits" => { + let _value_port = self.md.unwrap_port(PortID::from_hidden_value(0), true, "value"); + let _bits_port = self.md.unwrap_port(PortID::from_hidden_value(1), false, "bits"); + for i in 0..32 { + write!(self.program_text, "\tassign bits[{i}] = value[{i}];\n").unwrap(); + } + } + "BitsToInt" => { + let _bits_port = self.md.unwrap_port(PortID::from_hidden_value(0), true, "bits"); + let _value_port = self.md.unwrap_port(PortID::from_hidden_value(1), false, "value"); + for i in 0..32 { + write!(self.program_text, "\tassign value[{i}] = bits[{i}];\n").unwrap(); + } + } + other => panic!("Unknown Builtin: \"{other}\"! Do not mark modules as __builtin__ yourself!") + } + } - writeln!(self.program_text, "endmodule\n")?; - - Ok(()) + fn write_endmodule(&mut self) { + writeln!(self.program_text, "endmodule\n").unwrap(); } } +impl Module { + fn unwrap_port(&self, port_id : PortID, is_input : bool, name : &str) -> &Port { + let result = &self.ports[port_id]; + + assert_eq!(result.name, name); + assert_eq!(result.is_input, is_input); + + result + } +} + impl RealWireDataSource { fn get_sv_info_doc(&self) -> &str { match self { @@ -323,18 +404,7 @@ pub fn gen_verilog_code(md: &Module, instance: &InstantiatedModule, use_latency: use_latency, needed_untils: instance.compute_needed_untils() }; - ctx.write_verilog_code().unwrap(); + ctx.write_verilog_code(); program_text } - -pub fn mangle(str: &str) -> String { - let mut result = String::with_capacity(str.len()); - for c in str.chars() { - if c.is_whitespace() || c == ':' { - continue; - } - result.push(if c.is_alphanumeric() { c } else { '_' }); - } - result -} diff --git a/src/flattening/flatten.rs b/src/flattening/flatten.rs index b8f1c1d..6e201ce 100644 --- a/src/flattening/flatten.rs +++ b/src/flattening/flatten.rs @@ -1574,18 +1574,26 @@ pub fn flatten_all_modules(linker: &mut Linker) { let mut cursor = Cursor::new_at_root(&file.tree, &file.file_text); - cursor.list(kind!("source_file"), |cursor| match cursor.kind() { - kind!("module") => { - let Some(NameElem::Module(module_uuid)) = associated_value_iter.next() else { - unreachable!() - }; - - flatten(linker_ptr, *module_uuid, cursor); - } - other => todo!( - "{}", - tree_sitter_sus::language().node_kind_for_id(other).unwrap() - ), + cursor.list(kind!("source_file"), |cursor| { + cursor.go_down(kind!("source_obj"), |cursor| { + // Skip because we covered it in initialization. + let _ = cursor.optional_field(field!("extern_marker")); + + cursor.field(field!("object")); + match cursor.kind() { + kind!("module") => { + let Some(NameElem::Module(module_uuid)) = associated_value_iter.next() else { + unreachable!() + }; + + flatten(linker_ptr, *module_uuid, cursor); + } + other => todo!( + "{}", + tree_sitter_sus::language().node_kind_for_id(other).unwrap() + ), + } + }); }); span_debugger.defuse(); } diff --git a/src/flattening/initialization.rs b/src/flattening/initialization.rs index 76f36af..a44500b 100644 --- a/src/flattening/initialization.rs +++ b/src/flattening/initialization.rs @@ -1,5 +1,6 @@ use sus_proc_macro::{field, kind, kw}; +use crate::linker::IsExtern; use crate::prelude::*; use crate::linker::{checkpoint::CheckPoint, FileBuilder, LinkInfo, ResolvedGlobals}; @@ -224,53 +225,70 @@ pub fn gather_initial_file_data(mut builder: FileBuilder) { kind!("source_file"), &builder.other_parsing_errors, |cursor| { - let (kind, span) = cursor.kind_span(); - match kind { - kind!("module") => { - let parsing_errors = ErrorCollector::new_empty(builder.file_id, builder.files); - cursor.report_all_decendant_errors(&parsing_errors); - cursor.go_down_no_check(|cursor| { - let mut ctx = ModuleInitializationContext { - ports: FlatAlloc::new(), - interfaces: FlatAlloc::new(), - domains: FlatAlloc::new(), - template_inputs: FlatAlloc::new(), - file_text: builder.file_text, - }; + cursor.go_down(kind!("source_obj"), |cursor| { + let extern_kw = cursor.optional_field(field!("extern_marker")).then(|| cursor.kind()); + cursor.field(field!("object")); + + let (kind, span) = cursor.kind_span(); + match kind { + kind!("module") => { + let parsing_errors = ErrorCollector::new_empty(builder.file_id, builder.files); + cursor.report_all_decendant_errors(&parsing_errors); + cursor.go_down_no_check(|cursor| { + initialize_module(&mut builder, extern_kw, parsing_errors, span, cursor); + }); + } + _other => cursor.could_not_match(), + } + }); + }, + ); +} - let (name_span, name) = ctx.gather_initial_module(cursor); +fn initialize_module(builder: &mut FileBuilder, extern_kw : Option, parsing_errors: ErrorCollector, span: Span, cursor: &mut Cursor) { + let mut ctx = ModuleInitializationContext { + ports: FlatAlloc::new(), + interfaces: FlatAlloc::new(), + domains: FlatAlloc::new(), + template_inputs: FlatAlloc::new(), + file_text: builder.file_text, + }; - let resolved_globals = ResolvedGlobals::empty(); - let errors = parsing_errors.into_storage(); - let after_initial_parse_cp = - CheckPoint::checkpoint(&errors, &resolved_globals); + let (name_span, name) = ctx.gather_initial_module(cursor); - let md = Module { - link_info: LinkInfo { - documentation: cursor.extract_gathered_comments(), - file: builder.file_id, - name, - name_span, - span, - errors, - resolved_globals, - template_arguments: ctx.template_inputs, - after_initial_parse_cp, - after_flatten_cp: None, - }, - instructions: FlatAlloc::new(), - ports: ctx.ports, - domain_names: ctx.domains, - domains: FlatAlloc::new(), - interfaces: ctx.interfaces, - instantiations: InstantiationList::new(), - }; + let resolved_globals = ResolvedGlobals::empty(); + let errors = parsing_errors.into_storage(); + let after_initial_parse_cp = + CheckPoint::checkpoint(&errors, &resolved_globals); - builder.add_module(md); - }); - } - _other => cursor.could_not_match(), - } + let is_extern = match extern_kw { + None => IsExtern::Normal, + Some(kw!("extern")) => IsExtern::Extern, + Some(kw!("__builtin__")) => IsExtern::Builtin, + Some(_) => cursor.could_not_match() + }; + + let md = Module { + link_info: LinkInfo { + documentation: cursor.extract_gathered_comments(), + file: builder.file_id, + name, + name_span, + span, + errors, + is_extern, + resolved_globals, + template_arguments: ctx.template_inputs, + after_initial_parse_cp, + after_flatten_cp: None, }, - ); + instructions: FlatAlloc::new(), + ports: ctx.ports, + domain_names: ctx.domains, + domains: FlatAlloc::new(), + interfaces: ctx.interfaces, + instantiations: InstantiationList::new(), + }; + + builder.add_module(md); } diff --git a/src/instantiation/latency_count.rs b/src/instantiation/latency_count.rs index df7a345..9458807 100644 --- a/src/instantiation/latency_count.rs +++ b/src/instantiation/latency_count.rs @@ -135,7 +135,7 @@ impl InstantiatedModule { /// /// If needed only the same cycle it is generated, then this is equal to [RealWire::absolute_latency]. pub fn compute_needed_untils(&self) -> FlatAlloc { - let mut result = self.wires.map(|(id, w)| w.absolute_latency); + let mut result = self.wires.map(|(_id, w)| w.absolute_latency); for (_id, w) in &self.wires { w.source.iter_sources_with_min_latency(|other, _| { diff --git a/src/linker/mod.rs b/src/linker/mod.rs index 5df9b85..be3f0c5 100644 --- a/src/linker/mod.rs +++ b/src/linker/mod.rs @@ -72,6 +72,13 @@ impl Documentation { } } +#[derive(Debug)] +pub enum IsExtern { + Normal, + Extern, + Builtin +} + #[derive(Debug)] pub struct LinkInfo { pub file: FileUUID, @@ -81,6 +88,7 @@ pub struct LinkInfo { pub documentation: Documentation, pub errors: ErrorStore, pub resolved_globals: ResolvedGlobals, + pub is_extern : IsExtern, pub template_arguments: TemplateInputs, diff --git a/src/main.rs b/src/main.rs index 6ba642c..ae68f5b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,11 +119,15 @@ fn main() -> Result<(), Box> { } if let Some(md_name) = &config.codegen_module_and_dependencies_one_file { - let md = linker + let Some(md) = linker .modules .iter() - .find(|(_, md)| &md.link_info.name == md_name) - .unwrap(); + .find(|(_, md)| &md.link_info.name == md_name) else { + + let mut err_lock = std::io::stderr().lock(); + writeln!(err_lock, "Unknown module {md_name}").unwrap(); + std::process::exit(1); + }; codegen_with_dependencies(&linker, md.1, &format!("{md_name}_standalone")); } diff --git a/test.sus b/test.sus index eebee8a..59c2527 100644 --- a/test.sus +++ b/test.sus @@ -1084,4 +1084,3 @@ module run_instruction { // ... } } - diff --git a/tinyTestFile.sus b/tinyTestFile.sus index 4476861..b4b055f 100644 --- a/tinyTestFile.sus +++ b/tinyTestFile.sus @@ -1,14 +1,6 @@ +extern module TestExtern { + input int in_a'3 -module testNeedUntil { - - bool b'0 - - - bool c - - - reg reg c = b - + output int in_b'5 } - diff --git a/tree-sitter-sus b/tree-sitter-sus index 898549f..da2bc41 160000 --- a/tree-sitter-sus +++ b/tree-sitter-sus @@ -1 +1 @@ -Subproject commit 898549fe5253b0981a50234bcc811812cccd0ff0 +Subproject commit da2bc416b97ee66fd3c21937222891fb1bd0da5e